java线程安全volatile和synchronized关键字总结

volatile:

volatile关键字主要有两个作用:1是可以禁止指令的重排序优化,2.是提供多线程访问共享变量的内存可见性,所谓的内存可见性,也就是线程在访问时,都能看得到,属于公开透明的范围。Java中支持多个线程可以同时访问一个对象或者对象的成员变量,也就是存在共享性。共享变量的使用存在一致性的问题,对于多线程开发中,可以使用不同级别的锁来解决它,但锁的使用也有他的优缺点。而关键字volatile可以优化这一过程,它是用来修饰字段或成员变量,来说明此变量的访问都是从内存中获取,并且对它的改变必须同时回写到共享内存中,来保证所有线程对变量访问的可见性。

对于volatile变量修饰的共享变量进行写操作的时候会引发两件事情:1.将当前处理器缓存行的数据写回到系统内存。2写个写会内存的操作会使在其他CPU里缓存的该内存地址的数据无效。一般来说,每个线程可以拥有共享变量的拷贝,目的是用来加速程序的执行。处理器为了提高处理速度,一般不和内存进行通信,而是先将系统内存的数据读到内部缓存后进行操作,但操作完不知道何时会写会内存。在实现读写的过程中,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,在处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。

volatile保持内存可见性的特殊规则就是:read、load、use动作必须连续出现,assign、store、write动作必须连续出现。volatile关键字通过提供“内存屏障”的方式来防止指令被重排序,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。虽然volatile可以解决内存可见性问题和指令重排序问题,但是volatile可能产生jvm代码的优化问题,效率可能降低。在访问volatile变量时,不会进行加锁,也不会阻塞线程;volatile无法保证原子性;从同步机制来看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。

synchronized:

synchronized关键字是java提供的锁机制,主要解决线程的同步问题,它可以用来修饰方法和同步代码块,对于什么时候用同步代码块和方法,一般是看锁对象的范围。synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的,但是监视器锁本质又依赖于底层的操作系统的互斥锁(Mutex Lock)来实现的。而操作系统实现线程之间的切换就需要从用户态转换为核心态,状态之间的转换需要较长的时间,所以synchronized效率比较低,这用依赖于操作系统互斥所(Mutex Lock)所实现的锁成为重量级锁。

java线程安全volatile和synchronized关键字总结_第1张图片

使用synchronized的简单实例

public class MyThread extends Thread {
    private int count = 5;

    public synchronized void run(){
        count--;
        System.out.println(this.currentThread().getName()+"count:"+count);
    }
    
    public static void main(String[] args){
        MyThread thread = new MyThread();
        Thread thread1 = new Thread(thread,"thread1");
        Thread thread2 = new Thread(thread,"thread2");
        Thread thread3 = new Thread(thread,"thread3");
        Thread thread4 = new Thread(thread,"thread4");
        Thread thread5 = new Thread(thread,"thread5");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

    }

}

最后结果

thread1count:4
thread5count:3
thread4count:2
thread3count:1
thread2count:0

当多个线程访问run方法的时候,如果使用了synchronized修饰,那多线程会以排队的方式进行处理。而一个线程想要执行synchronized修饰的方法里的代码,首先尝试获得锁,如果拿到锁,执行synchronized代码体的内容,如果拿不到锁的话,这个线程就会不断地尝试获得这把锁,直到拿到为止。

你可能感兴趣的:(Java)