对象及变量的并发访问

synchronized同步方法

非线程安全问题会在多个线程对同一个对象中的实例变量进行并发访问,产生的后果是“脏读”,也就是读取到的数据是被更改过的。而线程安全是指获得实例变量的值是经过同步处理的,不会出现脏读的现象。
方法内的变量为线程安全;如果多个线程共同访问同一个对象中的实例变量,可能出现线程非安全问题
同步synchronized在字节码指令中的原理:字节码中使用monitorenter和monitorexit进行同步处理
同步:按照顺序执行A和B两个业务
异步:执行A业务的时候,B业务也在同步执行

两个线程调用同一对象的同一方法

public class MyObject {
    synchronized public void methodA(){
        try{
            System.out.println("begin methodA threadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end!");
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private MyObject object;
    public ThreadA(MyObject myObject){
        super();
        this.object=myObject;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}

public class ThreadB extends Thread {
    private MyObject object;
    public ThreadB(MyObject myObject){
        super();
        this.object=myObject;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}

public class Run {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        ThreadA threadA = new ThreadA(myObject);
        ThreadB threadB = new ThreadB(myObject);
        threadA.setName("a");
        threadB.setName("b");
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述

两个线程调用同一对象不同方法

同步方法、异步方法

public class MyObject {
    synchronized public void methodA(){
        try{
            System.out.println("begin methodA threadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end!");
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public void methodB(){
        try{
            System.out.println("begin methodB threadName="+Thread.currentThread().getName());
            Thread.sleep(5000);
            System.out.println("end!");
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class ThreadA extends Thread {
    private MyObject object;
    public ThreadA(MyObject myObject){
        super();
        this.object=myObject;
    }

    @Override
    public void run() {
        super.run();
        object.methodA();
    }
}

public class ThreadB extends Thread {
    private MyObject object;
    public ThreadB(MyObject myObject){
        super();
        this.object=myObject;
    }

    @Override
    public void run() {
        super.run();
        object.methodB();
    }
}

public class Run {
    public static void main(String[] args) {
        MyObject myObject = new MyObject();
        ThreadA threadA = new ThreadA(myObject);
        ThreadB threadB = new ThreadB(myObject);
        threadA.setName("a");
        threadB.setName("b");
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述
结论:

  1. A线程先持有object对象的锁,B线程可以异步调用object对象的非synchronized类型的方法
  2. A线程先持有object对象的锁,B线程如果在这时调用object对象中的synchronized类型方法,则需要等待,也就是同步
  3. 在方法声明添加synchronized并不是锁方法,而是锁当前类的对象
  4. 在Java中只用“将对象作为锁”这种说法,没有“锁方法”的说法
  5. 在Java中,“锁”就是“对象”,“对象”可以映射为“锁”,哪个线程拿到这个锁,哪个线程就可以执行这个对象中的synchronized同步方法
  6. 如果在X对象中使用了synchronized关键字声明非静态方法,则X对象就被当成锁

两个同步方法

多个线程执行同一业务对象中的不同同步方法时,是按照同步的方式调用的,只有当一个线程将对象锁进行释放后另一个线程才可以该对象锁调用其其他的同步方法

synchronized 锁重入

即在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时事可以得到该对象锁的;“可重入锁”是指自己可以再次获得自己的内部锁,例如,一个线程获得了某个对象锁,此时这个对象还没有释放,当其再次想要获取这个对象锁时还是可以获取的

public class Service {
    synchronized public void service1(){
        System.out.println("service1");
        service2();
    }
    synchronized public void service2(){
        System.out.println("service2");
        service3();
    }
    synchronized public void service3(){
        System.out.println("service3");
    }
}

public class Test3 extends Thread{
    @Override
    public void run() {
        super.run();
        Service service = new Service();
        service.service1();
    }

    public static void main(String[] args) {
        Test3 test3 = new Test3();
        test3.start();
    }
}

在这里插入图片描述

锁重入支持继承环境

“锁”的归属者不是“类”而是“实例”,不管这个方法是定义在父类还是子类中,执行时的锁都是那一个“实例”的,即通过supper调用父类的同步方法仍是获得子类实例的锁。

异常和锁

当一个线程出现异常时,会自动释放其持有的锁
类Thread.java中的suspend方法和sleep方法被调用后并不释放锁

重写synchronized 方法

重写方法如果不使用synchronized关键字,即非是同步方法,使用后变成同步方法

holdsLock方法

public class Test4 {
    public static void main(String[] args) {
        System.out.println("A  "+Thread.currentThread().holdsLock(Test4.class));
        synchronized (Test4.class){
            System.out.println("B  "+Thread.currentThread().holdsLock(Test4.class));
        }
        System.out.println("C  "+Thread.currentThread().holdsLock(Test4.class));
    }
}

在这里插入图片描述

synchronized 同步代码块

当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块
当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块,不在synchronized代码块中就是异步执行,在synchronized代码块中就是同步执行

锁定当前对象

package p113;

public class Task {
    synchronized public void otherMethod(){
        System.out.println("-------------run otherMethod");
    }

    public void doLongTimeTask(){
        synchronized (this){
            for (int i = 0; i < 10000; i++) {
                System.out.println("synchronized threadName="+ Thread.currentThread().getName()+"i="+(i+1));
            }
        }
    }
}

package p113;

public class MyThread1 extends Thread {
    private Task task;
    public MyThread1(Task task){
        super();
        this.task=task;
    }

    @Override
    public void run() {
        super.run();
        task.doLongTimeTask();
    }
}

package p113;

public class MyThread2 extends Thread {
    private Task task;
    public MyThread2(Task task){
        super();
        this.task=task;
    }

    @Override
    public void run() {
        super.run();
        task.otherMethod();
    }
}

package p113;

public class Run {
    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        MyThread1 myThread1 = new MyThread1(task);
        MyThread2 myThread2 = new MyThread2(task);
        myThread1.start();
        myThread2.start();
    }
}

在这里插入图片描述

任意对象作为锁

Java支持将“任意对象”作为锁来实现同步的功能,这个“任意对象”大多数是实例变量及方法的参数,即 synchronized(非this对象)

package P115;

public class Service {
    private String username;
    private String password;
    private String anything=new String();

    public void setUsernamePassword(String username,String password){
        try{
            synchronized (anything){
                System.out.println("线程名称:"+Thread.currentThread().getName()+"  "+System.currentTimeMillis()+"  进入同步块");
                this.username=username;
                Thread.sleep(3000);
                this.password=password;
                System.out.println("线程名称:"+Thread.currentThread().getName()+"  "+System.currentTimeMillis()+"  离开同步块");
            }
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

package P115;

public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        super();
        this.service=service;
    }

    @Override
    public void run() {
        super.run();
        service.setUsernamePassword("a", "aa");
    }
}

package P115;

public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        super();
        this.service=service;
    }

    @Override
    public void run() {
        super.run();
        service.setUsernamePassword("b", "bb");
    }
}

package P115;

public class Run {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        ThreadB threadB = new ThreadB(service);
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述
锁非this对象具有一定的优点:如果一个类中有很多个synchronized方法,则这时虽然能实现同步,但影响运行效率,如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,因为有两把锁,不与其他锁this同步方法争抢this锁,可以大大提高运行效率

类Class的单例性

每一个 *.java文件对应的Class类的实例都是一个,在内存中是单例的,Class类用于描述类的基本信息,包括有多少字段,有多少构造方法,有多少普通方法等

静态同步synchronized方法

关键字synchronized还可以应用在static静态方法上,表示对当前Class类进行加锁,Class类的对象都是单例的;更准取得说,在当前静态static方法上使用synchronized关键字声明同步方法时,使用当前静态方法所在的类对应的Class类的单例对象作为锁

package p135;

import P115.ThreadB;

public class Service {
    synchronized public static void printA(){
        try{
            System.out.println("线程名称:"+ Thread.currentThread().getName()+" "+System.currentTimeMillis()+"进入printA");
            Thread.sleep(3000);
            System.out.println("线程名称:"+Thread.currentThread().getName()+" "+System.currentTimeMillis()+"离开printA");
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    synchronized public static void printB(){
        System.out.println("线程名称:"+Thread.currentThread().getName()+" "+System.currentTimeMillis()+"  进入printB");
        System.out.println("线程名称:"+Thread.currentThread().getName()+" "+System.currentTimeMillis()+"  离开printB");
    }
}

package p135;

public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        super();
        this.service=service;
    }

    @Override
    public void run() {
        super.run();
        service.printA();
    }
}

package p135;

public class ThreadB extends Thread {
    private Service service;
    public ThreadB(Service service){
        super();
        this.service=service;
    }

    @Override
    public void run() {
        super.run();
        service.printB();
    }
}

package p135;

public class Run {
    public static void main(String[] args) {
        Service service1 = new Service();
        Service service2 = new Service();
        ThreadA threadA = new ThreadA(service1);
        ThreadB threadB = new ThreadB(service2);
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述

synchronized(class)代码块

同步synchronized(class) 代码块的作用和synchronized static 方法的作用一样
将service类代码进行修改,其他不变,运行结果不变

package p135;

import P115.ThreadB;

public class Service {
    public void printA(){
        synchronized (Service.class){
            try{
                System.out.println("线程名称:"+ Thread.currentThread().getName()+" "+System.currentTimeMillis()+"  进入printA");
                Thread.sleep(3000);
                System.out.println("线程名称:"+Thread.currentThread().getName()+" "+System.currentTimeMillis()+"  离开printA");
            }
            catch (InterruptedException e){
                e.printStackTrace();
            }
        }

    }

    public void printB(){
        synchronized (Service.class){
            System.out.println("线程名称:"+Thread.currentThread().getName()+" "+System.currentTimeMillis()+"  进入printB");
            System.out.println("线程名称:"+Thread.currentThread().getName()+" "+System.currentTimeMillis()+"  离开printB");
        }

    }
}

String常量池和同步

当 synchronized(string)同步块和String联合使用时,要注意常量池带来的一些意外,如下String的两个值都是“AA”,两个线程持有相同的锁,造成B线程无法执行,大多数情况下同步代码块不使用String作为锁

package P139;

import P115.ThreadB;
import com.sun.corba.se.spi.orb.StringPair;
import com.sun.org.apache.xerces.internal.dom.PSVIAttrNSImpl;
import sun.swing.plaf.synth.DefaultSynthStyle;

public class Service {
    public static void print(String stringpram){
        try{
            synchronized (stringpram){
                while(true){
                    System.out.println(Thread.currentThread().getName());
                    Thread.sleep(1000);
                }
            }
        }
        catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

package P139;

public class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service){
        super();
        this.service=service;
    }

    @Override
    public void run() {
        super.run();
        service.print("AA");
    }
}

package P139;

public class ThreadB extends Thread {
        private Service service;
        public ThreadB(Service service){
            super();
            this.service=service;
        }

        @Override
        public void run() {
            super.run();
            service.print("AA");
        }
}

package P139;

public class Run {
    public static void main(String[] args) {
        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadB.start();
    }
}

在这里插入图片描述

多线程死锁

package P147;

import P139.ThreadB;

public class DeadThread implements Runnable {
    public String username;
    public Object lock1=new Object();
    public Object lock2=new Object();

    public void setFlag(String username){
        this.username=username;
    }
    @Override
    public void run() {
        if(username.equals("a")){
            synchronized (lock1){
                try{
                    System.out.println("username="+username);
                    Thread.sleep(3000);
                }
                catch (InterruptedException e){
                    e.printStackTrace();
                }
                synchronized (lock2){
                    System.out.println("lock1->lock2代码执行");
                }
            }
        }
        else{
            synchronized (lock2){
                try{
                    System.out.println("username="+username);
                    Thread.sleep(3000);
                }
                catch (InterruptedException e){
                    e.printStackTrace();
                }
                synchronized (lock1){
                    System.out.println("lock2->lock1顺利执行");
                }


            }
        }

    }
}

package P147;

import jdk.nashorn.internal.ir.CatchNode;

public class Run {
    public static void main(String[] args) {
        try {
            DeadThread deadThread = new DeadThread();
            deadThread.setFlag("a");
            Thread thread1 = new Thread(deadThread);
            thread1.start();
            thread1.sleep(100);
            deadThread.setFlag("b");
            Thread thread2 = new Thread(deadThread);
            thread2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

在这里插入图片描述

volatile 关键字

可见性

volatile关键字具有可见性,可见性是指A线程更改变量的值后,B线程马上就能看到更改后的变量的值

  1. 多线程出现死循环
package P163;

public class RunThread extends Thread {
    private boolean isRunnig=true;
    public boolean getRunnig(){
        return isRunnig;
    }
    public void setRunnig(boolean isRunnig){
        this.isRunnig=isRunnig;
    }

    @Override
    public void run() {
        super.run();
        System.out.println("进入running!");
        while(isRunnig==true){
        }
        System.out.println("推出running!");
    }
}

package P163;

public class Run {
    public static void main(String[] args) throws InterruptedException {
        RunThread runThread = new RunThread();
        runThread.start();
        Thread.sleep(1000);
        runThread.setRunnig(false);
        System.out.println("已经赋值为false");
    }
}

在这里插入图片描述
2. 原因分析
在启动RunThread线程时,变量isRunning=true存在于公共堆栈及私有堆栈中,线程运行后一直在线程的私有堆栈中取得isRunning的值为true,而代码runThread.setRunnig(false)虽然被执行,但更新的却是公共堆栈中的isRunning的值,因为两块内存的值不一致,导致线程一直处于死循环
3.使用volatile解决
volatile private boolean isRunnig=true
在这里插入图片描述
通过volatile关键字,可以强制从公共内存中取值,volatile关键字可以增强实例变量在多个线程间的可见性

原子性

在32位系统中,未使用volatile声明的long或者double数据类型没有实现写原子性,使用volatile声明,则实现了写原子性;在X86架构64位JDK中,写double和long实现了原子性

禁止代码重排

使用volatile关键字可以禁止代码重排序,在Java代码运行时,JIT(Just-in-time compiler,即时编译器)可以动态改变程序代码运行的顺序。例如对于没有依赖关系的A、B、C、、D四段代码,B、D耗时少,A、C耗时多,假设CPU流水线同时执行这四条代码,为了提高运行效率,JIT可能会代码重排,BD先执行然后执行AC,使用volatile关键字可以禁止代码重排,换而言之,volatile操作就相当于一个屏障,volatile操作之前的可以重排,之后的也可以重排,但volatile操作之前的代码不能排到volatile操作之后的代码的后面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现 Cookie 的并发访问,可以使用多线程技术来实现。具体步骤如下: 1. 定义一个 Cookie 对象,用于存储 Cookie 信息。 2. 定义一个共享变量,用于存储所有的 Cookie 对象。 3. 创建多个线程,每个线程都可以访问共享变量。 4. 当一个线程需要访问 Cookie 对象时,首先需要获取共享变量的锁,以确保其他线程不会修改共享变量。 5. 然后,该线程可以读取或修改 Cookie 对象,完成操作后,释放共享变量的锁。 下面是一个示例代码,演示如何实现 Cookie 的并发访问: ```python import threading class Cookie: def __init__(self, name, value): self.name = name self.value = value # 共享变量,存储所有的 Cookie 对象 cookies = [] # 读取和修改 cookies 的函数 def read_cookies(): with lock: for cookie in cookies: print(cookie.name, cookie.value) def modify_cookies(): with lock: for cookie in cookies: cookie.value = "modified" # 创建多个线程,每个线程都可以访问 cookies threads = [] for i in range(10): t = threading.Thread(target=read_cookies) threads.append(t) for i in range(10): t = threading.Thread(target=modify_cookies) threads.append(t) # 启动所有线程 lock = threading.Lock() for t in threads: t.start() # 等待所有线程执行完毕 for t in threads: t.join() ``` 上述代码中,创建了 10 个线程用于读取 cookies,和 10 个线程用于修改 cookies。通过 Lock 对象来保证多个线程对 cookies 的并发访问安全。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值