synchronized 与 volatile 关键字 |
您所在的位置:网站首页 › volatile可以保证 › synchronized 与 volatile 关键字 |
目录
1.前言1.synchronized 关键字1. 互斥2.保证内存可见性3.可重入
2. volatile 关键字1.保证内存可见性2.无法保证原子性
3.synchronized 与 volatile 的区别
1.前言
synchronized关键字和volatile是大家在Java多线程学习时接触的两个关键字,很多同学可能学习完就忘记了,本文帮助大家回顾以及学习两个关键字的作用,以及说出它们的区别,同时也为了自己学习巩固。 1.synchronized 关键字 1. 互斥属于synchronized最关键的特性,可以起到互斥的作用,当某个线程执行到某个对象的synchronized中时,其他线程如果也执行到同一个对象 的synchronized 时就会进行阻塞等待。 进入synchronized 修饰的代码块此时相当于 加锁退出synchronized 修饰的代码块此时相当于 释放锁其解决的问题是在多线程环境下,多个线程对于同一个变量进行读写操作时可能产生的线程安全问题。 如下图代码: public class Main { static int count = 0; static void add() { count++; } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i for (int i = 0; i count++; }再次运行后答案与预期相符合: 从上面也可以看出synchronized的工作过程: 1.获得互斥锁2.从主内存拷贝变量的最新副本到工作内存3.执行代码4.讲更改后的共享变量的值更新回工作内存5.释放互斥锁这样的工作流程,是一定可以保证内存可见性的。当然有的同学并不了解什么是内存可见性,下文讲volatile时我们稍微讲一下,因为它也能保证内存可见性。 3.可重入synchronized同步块,对于同一条线程来说是可重入的,不会出现将自身锁死的情况。 当然大家可能对 自身锁死 这个情况不太理解,我们举例一个代码: public class Main { //锁对象 public static Object lock = new Object(); public static void main(String[] args) { //一次加锁 synchronized (lock) { //二次加锁 synchronized (lock) { System.out.println("正确输出"); } } } }当线程在一次加锁时,会成功加锁,当第二次加锁时,此时lock已被上锁,于是该线程进行阻塞等待,但其实这个锁是被它自己拿着的,它又不进行释放锁操作,于是将自己锁死。这样的锁称之为 不可重入锁。 当我们Java中的synchronized是可重入锁,不会出现上面的问题,它可以正确打印: 在上诉代码中: increase和increase2两个方法都加了synchronized ,而且它们的锁对象都是针对当前对象加锁的。在调用increase2时,会先给该对象上锁,执行调用increase时,会二次上锁(此时上个锁还未释放),这是没问题的,因为synchronized是可重入锁。那是否真的上了两把锁呢? 其实并非如此,在可重入锁的内部,包含了 线程持有者 和 计数器 两个信息。 如果某个线程加锁时,发现锁已被占用,但又发现占用的恰好是自己时,那么然后可以获取到这个锁,并让计数器自增解锁时首先会让计数器自减,但只有真正自减到0时,我们才会真正意义上的将该锁释放,以供其他线程获取到。 2. volatile 关键字相对于 synchronized来说,大家可能对volatile会比较陌生,我们来看看其有哪些作用。 1.保证内存可见性
当一个线程大量地从主内存请求同一个变量的值时,它会发现这个值一直没变,此时jvm会 “自作主张” 的进行优化,直接从工作内存读取之前读到的值。这就会导致一个问题,其他线程对这个共享变量值进行修改,这个线程不能及时地被看到,也就读到了一个错误的值。 比如如下代码: public class Main{ static int isQuit = 0; public static void main(String[] args) { Thread t = new Thread(() -> { while (isQuit == 0) { } System.out.println("t线程执行结束"); }); t.start(); Scanner sc = new Scanner(System.in); isQuit = sc.nextInt(); System.out.println("main线程执行结束"); } }执行以后随便输入一个非零整数: 解决的方法也很简单,只需要给isQuit加上volatile关键字,这样每次t线程都会强制去主内存中读取isQuit的值,从而保证了内存可见性。 2.无法保证原子性volatile相较于synchronized来说,主要在于其无法保证原子性,也就是对于下面这个程序,即使给count加上volatile,我们也无法让count的值为100000。 public class Main { static int count = 0; static void add() { count++; } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i for (int i = 0; i |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |