synchronized还可以应用在静态方法上,如果这么写,则代表的是对当前.java文件对应的Class类加锁。看一下例子,注意一下printC()并不是一个静态方法:

public class ThreadDomain25
{public synchronized 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();}}public synchronized static void printB(){System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printB()方法");System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printB()方法");}public synchronized void printC(){System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printC()方法");System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printC()方法");}
}

写三个线程分别调用这三个方法:

public class MyThread25_0 extends Thread
{public void run(){ThreadDomain25.printA();}
}

public class MyThread25_1 extends Thread
{public void run(){ThreadDomain25.printB();}
}

public class MyThread25_2 extends Thread
{private ThreadDomain25 td;public MyThread25_2(ThreadDomain25 td){this.td = td;}public void run(){td.printC();}
}

写个main函数启动这三个线程:

public static void main(String[] args)
{ThreadDomain25 td = new ThreadDomain25();MyThread25_0 mt0 = new MyThread25_0();MyThread25_1 mt1 = new MyThread25_1();MyThread25_2 mt2 = new MyThread25_2(td);mt0.start();mt1.start();mt2.start();
}

看一下运行结果:

线程名称为:Thread-0在1443857019710进入printA()方法
线程名称为:Thread-2在1443857019710进入printC()方法
线程名称为:Thread-2在1443857019710离开printC()方法
线程名称为:Thread-0在1443857022710离开printA()方法
线程名称为:Thread-1在1443857022710进入printB()方法
线程名称为:Thread-1在1443857022710离开printB()方法

从运行结果来,对printC()方法的调用和对printA()方法、printB()方法的调用时异步的,这说明了静态同步方法和非静态同步方法持有的是不同的锁,前者是类锁,后者是对象锁

所谓类锁,举个再具体的例子。假如一个类中有一个静态同步方法A,new出了两个类的实例B和实例C,线程D持有实例B,线程E持有实例C,只要线程D调用了A方法,那么线程E调用A方法必须等待线程D执行完A方法,尽管两个线程持有的是不同的对象。

volatile关键字

直接先举一个例子:

public class MyThread28 extends Thread
{private boolean isRunning = true;public boolean isRunning(){return isRunning;}public void setRunning(boolean isRunning){this.isRunning = isRunning;}public void run(){System.out.println("进入run了");while (isRunning == true){}System.out.println("线程被停止了");}
}

public static void main(String[] args)
{try{MyThread28 mt = new MyThread28();mt.start();Thread.sleep(1000);mt.setRunning(false);System.out.println("已赋值为false");}catch (InterruptedException e){e.printStackTrace();}
}

看一下运行结果:

进入run了
已赋值为false

也许这个结果有点奇怪,明明isRunning已经设置为false了, 线程还没停止呢?

这就要从Java内存模型(JMM)说起,这里先简单讲,虚拟机那块会详细讲的。根据JMM,Java中有一块主内存,不同的线程有自己的工作内存,同一个变量值在主内存中有一份,如果线程用到了这个变量的话,自己的工作内存中有一份一模一样的拷贝。每次进入线程从主内存中拿到变量值,每次执行完线程将变量从工作内存同步回主内存中。

出现打印结果现象的原因就是主内存和工作内存中数据的不同步造成的。因为执行run()方法的时候拿到一个主内存isRunning的拷贝,而设置isRunning是在main函数中做的,换句话说 ,设置的isRunning设置的是主内存中的isRunning,更新了主内存的isRunning,线程工作内存中的isRunning没有更新,当然一直死循环了,因为对于线程来说,它的isRunning依然是true。

解决这个问题很简单,给isRunning关键字加上volatile。加上了volatile的意思是,每次读取isRunning的值的时候,都先从主内存中把isRunning同步到线程的工作内存中,再当前时刻最新的isRunning。看一下给isRunning加了volatile关键字的运行效果:

进入run了
已赋值为false
线程被停止了

看到这下线程停止了,因为从主内存中读取了最新的isRunning值,线程工作内存中的isRunning变成了false,自然while循环就结束了。

volatile的作用就是这样,被volatile修饰的变量,保证了每次读取到的都是最新的那个值。线程安全围绕的是可见性原子性这两个特性展开的,volatile解决的是变量在多个线程之间的可见性,但是无法保证原子性

多提一句,synchronized除了保障了原子性外,其实也保障了可见性。因为synchronized无论是同步的方法还是同步的代码块,都会先把主内存的数据拷贝到工作内存中,同步代码块结束,会把工作内存中的数据更新到主内存中,这样主内存中的数据一定是最新的。

转载于:https://www.cnblogs.com/wangchaoyu/p/9106167.html

synchronized、volatile关键字相关推荐

  1. Java多线程6:synchronized锁定类方法、volatile关键字及其他

    同步静态方法 synchronized还可以应用在静态方法上,如果这么写,则代表的是对当前.java文件对应的Class类加锁.看一下例子,注意一下printC()并不是一个静态方法: public ...

  2. Java中的synchronized与volatile关键字

    原文出处:http://hukai.me/android-training-course-in-chinese/performance/smp/index.html Java中的"synch ...

  3. java 轻量级同步volatile关键字简介与可见性有序性与synchronized区别 多线程中篇(十二)...

    概念 JMM规范解决了线程安全的问题,主要三个方面:原子性.可见性.有序性,借助于synchronized关键字体现,可以有效地保障线程安全(前提是你正确运用) 之前说过,这三个特性并不一定需要全部同 ...

  4. java和线程相关的关键字有哪些_Java中有哪些机制来保证线程安全?synchronized关键字和volatile关键字...

    想要解决线程安全问题,首先要知道为什么会造成线程不安全? 在单线程中,我们从来没有提到个线程安全问题,线程安全问题是只出现在多线程中的一个问题.因为多线程情况下有共享数据,每个线程都共享这些数据并对这 ...

  5. 【说一千道一万】volatile关键字的作用和synchronized的关系

    [说一千道一万]volatile关键字的作用和synchronized的关系 volatile这个关键字在面试的时候通常会拿出来跟synchronized作比较,这也是比较基础的面试题目,这里自己总结 ...

  6. Java面试补给站——final、volatile、synchronized三大关键字

    如果说语法是一个编程语言的骨架,那么关键字就是骨架的关节. 在Java中有各种功能的关键字,最常用的int,break,public这些关键字都是在编程过程中必不可少的. 本文从面试提问的角度聊一下f ...

  7. Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字

    线程安全问题是多线程编程中最典型的一类问题之一.如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的. 通俗来说,线程不安全指的就是某一代 ...

  8. java中实现具有传递性吗_Java中volatile关键字详解,jvm内存模型,原子性、可见性、有序性...

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

  9. volatile关键字之全面深度剖析

    引言 volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字 ...

  10. 爆赞,对 volatile 关键字讲解最好的一篇文章!

    欢迎关注方志朋的博客,回复"666"获面试宝典 最近,在一篇文章中了解到了 volatile 关键字,在强烈的求知欲趋使下,我查阅了一些相关资料进行了学习,并将学习笔记记录如下,希 ...

最新文章

  1. 烂泥:下载酷我收费的MV
  2. 2012年第一篇博文——致谢:2011年中国IT十大杰出博客获奖感言
  3. 动画原理——绘制正弦函数环绕运动椭圆运动
  4. 前端学习(2861):简单秒杀系统学习之前端优化css
  5. 【MySQL】MySQL 中的函数
  6. c语言数据结构单链表输出链表操作,单链表一系列操作c语言实现(按照严蔚敏那本数据结构编写)...
  7. 安装linux没有raid驱动程序,LINUX 无法在 RAID 上安装的问题
  8. 探究VS2017运行库
  9. 浅析EL表达式注入漏洞
  10. 手写深度学习之优化器(SGD、Momentum、Nesterov、AdaGrad、RMSProp、Adam)
  11. 如何优化在搜索引擎上的友好度
  12. 大数据背景下的智慧物流:物流行业解决方案
  13. UEStudio 9 注册码
  14. ARTS-9(幸福的奥秘是什么?)
  15. 采购中心如何高效管理供应商的质量?
  16. Day09 - 面向对象进阶
  17. am命令发送广播以及查看已发送广播信息
  18. sqlserver 与access,excel互相导入导出代码
  19. 微信小程序原生常用语法 1
  20. JY901串口数据接收与处理(Python)

热门文章

  1. Security+ 学习笔记56 增强隐私保护的技术
  2. 报错解决:No module named tensorflow.contrib
  3. kotlin支持jdk1.8编译,使用Java8特性
  4. nmap +shell脚本实现内网端口巡检
  5. centos6 与 7 其中的一些区别
  6. 七牛2015结姻大典,约吗?
  7. vbs程序批量禁用域用户然后移动到指定OU
  8. [转载]android debug工具集(挺全的)
  9. CCIE试验备考之交换security
  10. bc8android汽车中控屛功能有哪些,安卓大屏功能强大 卡仕达顺车机一机全能