本篇随笔主要介绍 java 中 synchronized 关键字常用法,主要有以下四个方面:

  1、实例方法同步

  2、静态方法同步

  3、实例方法中同步块

  4、静态方法中同步块

  

  我觉得在学习synchronized关键字之前,我们首先需要知道以下一点:Java 中每个实例对象对应一把锁且每个实例对象只有一把锁,synchronized 关键字是通过对相应的实例对象加锁来实现同步功能的。

  

  一、实例方法中使用 synchronized 加锁

  实例方法中默认被加锁的对象是调用此方法的实例对象。

  

  1. class ImmutableValue {
  2. public synchronized void comeIn() throws InterruptedException{
  3. System.out.println(Thread.currentThread().getName() + ": start");
  4. Thread.sleep(5000);
  5. System.out.println(Thread.currentThread().getName() + ": finish");
  6. }
  7. public void synchronized comeInIn() throws InterruptedException {
  8. System.out.println(Thread.currentThread().getName() + ": start");
  9. Thread.sleep(5000);
  10. System.out.println(Thread.currentThread().getName() + ": finish");
  11. }
  12. }
  13. public class TestImmutableValue {
  14. public static void main(String[] args) {
  15. ImmutableValue im = new ImmutableValue();
  16. Thread t1 = new Thread(new Runnable() {
  17.  
  18. @Override
  19. public void run() {
  20. // TODO Auto-generated method stub
  21. try {
  22. im.comeIn();
  23. } catch (InterruptedException e) {
  24. // TODO Auto-generated catch block
  25. e.printStackTrace();
  26. }
  27. }
  28.  
  29. }, "t1");
  30. Thread t2 = new Thread(new Runnable() {
  31.  
  32. @Override
  33. public void run() {
  34. // TODO Auto-generated method stub
  35. try {
  36. im.comeInIn();
  37. } catch (InterruptedException e) {
  38. // TODO Auto-generated catch block
  39. e.printStackTrace();
  40. }
  41. }
  42.  
  43. }, "t2");
  44. t1.start();
  45. t2.start();
  46. }
  47. }

    在上面的代码中创建了两个线程并分别命名为 t1, t2。调用了同一个对象 im 的两个同步方法 comeIn 和 comeInIn, 执行结果如下:

      

    在 t1 线程开始执行后,即使 t1 线程睡眠了5s,线程 t2 中的 comeInIn 方法仍然没有得到执行。这是因为 t1 线程先执行的 comeIn 方法,持有了对象 im 的锁,且 comeIn 方法并没有执行完,对象 im 的锁没有被释放,所以 comeInIn 方法无法对对象 im 加锁,就无法继续执行,只能等到 t1 线程中的 comeIn 方法执行完毕,释放对象 im 的锁,comeInIn 方法才能继续执行。

    但是如果 t1 线程调用的是对象 im 的 comeIn 方法,而 t2 线程调用的是我们声明的另外一个  ImmutableValue 对象 im2 对象的 comeInIn 方法,则这两个方法的执行是互不影响的。因为 t1 线程的 comeIn 方法要获得 im 对象的锁,而 t2 线程要获得的是 im2 对象的锁,两个锁并不是同一个锁(Java中每个实例对象都有且只有一个锁),所以这两个方法执行互不影响。

    

  二、静态方法中使用 synchronized 加锁

  静态方法中默认被加锁的对象是此静态方法所在类的 class 对象。

  

  1. class staticMethodSynchronized {
  2. public static synchronized void method1() throws InterruptedException {
  3. System.out.println(Thread.currentThread().getName() + ": start");
  4. Thread.sleep(5000);
  5. System.out.println(Thread.currentThread().getName() + ": finish");
  6. }
  7. public static synchronized void method2() throws InterruptedException {
  8. System.out.println(Thread.currentThread().getName() + ": start");
  9. Thread.sleep(5000);
  10. System.out.println(Thread.currentThread().getName() + ": finish");
  11. }
  12. }
  13. public class TestStaticClassSynchronized {
  14. public static void main(String[] args) {
  15. Thread t1 = new Thread(new Runnable() {
  16.  
  17. @Override
  18. public void run() {
  19. // TODO Auto-generated method stub
  20. try {
  21. staticMethodSynchronized.method1();
  22. } catch (InterruptedException e) {
  23. // TODO Auto-generated catch block
  24. e.printStackTrace();
  25. }
  26. }
  27.  
  28. }, "t1");
  29. Thread t2 = new Thread(new Runnable() {
  30.  
  31. @Override
  32. public void run() {
  33. // TODO Auto-generated method stub
  34. try {
  35. staticMethodSynchronized.method2();
  36. } catch (InterruptedException e) {
  37. // TODO Auto-generated catch block
  38. e.printStackTrace();
  39. }
  40. }
  41.  
  42. }, "t2");
  43. t1.start();
  44. t2.start();
  45. }
  46. }

    在上述代码中创建了两个线程并命名为 t1,t2。 t1,t2 线程调用了 staticMethodSynchronized 类的两个静态同步方法 method1 和 method2。执行结果如下:

    

    在 t1 线程开始执行后,即使 t1 线程睡眠了5s,线程 t2 中的 method2 方法仍然没有得到执行。这是因为 t1 线程先执行的 method1 方法,持有了staticMethodSynchronized 类对象的锁,且 method1 方法并没有执行完,staticMethodSynchronized 类对象的锁没有被释放,所以 comeInIn 方法无法对staticMethodSynchronized 类对象加锁,就无法继续执行,只能等到 t1 线程中的 method1 方法执行完毕,释放 staticMethodSynchronized 类对象的锁,method2 方法才能继续执行。

  三、实例方法中使用 synchronized 关键字制造同步块

  同步块中默认被加锁的对象是此同步块括号声明中包含的对象。

  

  1. class ImmutableValue {
  2. public synchronized void comeIn() throws InterruptedException{
  3. System.out.println(Thread.currentThread().getName() + ": start");
  4. Thread.sleep(5000);
  5. System.out.println(Thread.currentThread().getName() + ": finish");
  6. }
  7. public void comeInIn() throws InterruptedException {
  8. System.out.println(Thread.currentThread().getName() + ": start");
  9. synchronized(this) {
  10.  
  11. }
  12. System.out.println(Thread.currentThread().getName() + ": finish");
  13. }
  14. }
  15. public class TestImmutableValue {
  16. public static void main(String[] args) {
  17. ImmutableValue im = new ImmutableValue();
  18. Thread t1 = new Thread(new Runnable() {
  19.  
  20. @Override
  21. public void run() {
  22. // TODO Auto-generated method stub
  23. try {
  24. im.comeIn();
  25. } catch (InterruptedException e) {
  26. // TODO Auto-generated catch block
  27. e.printStackTrace();
  28. }
  29. }
  30.  
  31. }, "t1");
  32. Thread t2 = new Thread(new Runnable() {
  33.  
  34. @Override
  35. public void run() {
  36. // TODO Auto-generated method stub
  37. try {
  38. im.comeInIn();
  39. } catch (InterruptedException e) {
  40. // TODO Auto-generated catch block
  41. e.printStackTrace();
  42. }
  43. }
  44.  
  45. }, "t2");
  46. t1.start();
  47. t2.start();
  48. }
  49. }

    由以上代码可以看到: 在 comeInIn 方法中,运用  synchronized(this) 制造同步块,要执行同步块内的代码,就必须获得 this 对象的锁(调用 comeInIn 方法的对象)。

    执行结果可能为:

    

    由此执行结果可见:t1 线程先执行了 comeIn 方法,获得了对象 im 的锁,之后由于 t1 线程进入睡眠状态,t2 线程得到运行,开始执行 comeInIn 方法,当执行到同步代码块时发现对象 im 已被加锁,无法继续执行。t1 线程睡眠结束之后继续执行,结束后释放对象 im 的锁,t2 线程才能继续执行。

  四、静态方法中使用 synchronized 关键字制造同步块

  同步块中默认被加锁的对象是此同步块括号声明中包含的对象。

  

  1. class staticMethodSynchronized {
  2. private static final Object OBJ = new Object();
  3. public static void method1() throws InterruptedException {
  4. System.out.println(Thread.currentThread().getName() + ": start");
  5. synchronized(OBJ) {
  6. System.out.println(Thread.currentThread().getName() + ": 获得锁");
  7. System.out.println(Thread.currentThread().getName() + ": 释放锁");
  8. }
  9. System.out.println(Thread.currentThread().getName() + ": finish");
  10. }
  11. public static void method2() throws InterruptedException {
  12. System.out.println(Thread.currentThread().getName() + ": start");
  13. synchronized(OBJ) {
  14. System.out.println(Thread.currentThread().getName() + ": 获得锁");
  15. System.out.println(Thread.currentThread().getName() + ": 释放锁");
  16. }
  17. System.out.println(Thread.currentThread().getName() + ": finish");
  18. }
  19. }
  20. public class TestStaticClassSynchronized {
  21. public static void main(String[] args) {
  22. Thread t1 = new Thread(new Runnable() {
  23.  
  24. @Override
  25. public void run() {
  26. // TODO Auto-generated method stub
  27. try {
  28. staticMethodSynchronized.method1();
  29. } catch (InterruptedException e) {
  30. // TODO Auto-generated catch block
  31. e.printStackTrace();
  32. }
  33. }
  34.  
  35. }, "t1");
  36. Thread t2 = new Thread(new Runnable() {
  37.  
  38. @Override
  39. public void run() {
  40. // TODO Auto-generated method stub
  41. try {
  42. staticMethodSynchronized.method2();
  43. } catch (InterruptedException e) {
  44. // TODO Auto-generated catch block
  45. e.printStackTrace();
  46. }
  47. }
  48.  
  49. }, "t2");
  50. t1.start();
  51. t2.start();
  52. }
  53. }

    在上述代码中,两个静态方法中的同步块都要获得对象 OBJ 的锁才能继续向下执行,执行结果可能如下:

       

    若 t1 线程先获得锁,则必须等到 t1 释放锁之后,t2 线程中同步代码块及其之后的代码才能继续执行,t2 线程先获得锁,t1 线程同理。

   总之,我认为我们只需抓住一点:Java 中每个实例对象对应一把锁且每个实例对象只有一把锁,synchronized 关键字是通过对相应的实例对象加锁来实现同步功能的(静态方法为对相应的 class 对象加锁)。在执行 synchronized 方法或 synchronized 同步块之前,我们只需判断其需要获得的对象的锁是否可获得,就可判断此方法或同步块是否可得到执行。

Java synchronized关键字用法(清晰易懂)的更多相关文章

  1. 巨人大哥谈Java中的Synchronized关键字用法

    巨人大哥谈Java中的Synchronized关键字用法 认识synchronized 对于写多线程程序的人来说,经常碰到的就是并发问题,对于容易出现并发问题的地方价格synchronized基本上就 ...

  2. synchronized关键字用法

    看到网上很多讲synchronized关键字用法的文章,说的都很有道理,也很深刻,但是看完总感觉脑袋里还是有点乱乱的.经过一番自己的思考后,想从自己的思考角度出发,来说一说synchronized关键 ...

  3. Java synchronized 关键字详解

    Java synchronized 关键字详解 前置技能点 进程和线程的概念 线程创建方式 线程的状态状态转换 线程安全的概念 synchronized 关键字的几种用法 修饰非静态成员方法 sync ...

  4. Java Synchronized 关键字

    本文内容 Synchronized 关键字 示例 Synchronized 方法 内部锁(Intrinsic Locks)和 Synchronization 参考资料 下载 Demo Synchron ...

  5. Java中的Synchronized关键字用法

    认识synchronized 对于写多线程程序的人来说,经常碰到的就是并发问题,对于容易出现并发问题的地方加上synchronized修饰符基本上就搞定 了,如果说不考虑性能问题的话,这一招绝对能应对 ...

  6. Java:synchronized关键字引出的多种锁

    前言 Java 中的 synchronized关键字可以在多线程环境下用来作为线程安全的同步锁.本文不讨论 synchronized 的具体使用,而是研究下synchronized底层的锁机制,以及这 ...

  7. java synchronized关键字浅探

    synchronized 是 java 多线程编程中用于使线程之间的操作串行化的关键字.这种措施类似于数据库中使用排他锁实现并发控制,但是有所不同的是,数据库中是对数据对象加锁,而 java 则是对将 ...

  8. java synchronized关键字

    引用其他人的一段话 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchro ...

  9. Java synchronized关键字的理解

    转载自:http://blog.csdn.net/xiao__gui/article/details/8188833 在Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环 ...

随机推荐

  1. 初探Stage3D(二) 了解AGAL

    关于本文 本文并无打算事无巨细的介绍一遍AGAL,仅仅是对现有文档的一些理解及汇总,所以请先阅读相参考文档 AGAL概念 参考资料 http://www.adobe.com/devnet/flashp ...

  2. saiku缓存整理

    使用saiku的人,肯定都有这么一个经历,查询了一次多维分析数据表,第二次之后就特别快,因为它缓存了结果,可问题是过了一天,甚至几天,来源数据早都更换了,可还是这个缓存结果.问题来了,缓存不失效! 那 ...

  3. Swift 字符与字符串

    Swift 的 String 和 Character 类型

  4. U3D4.X版本无法安装MONODEV编辑器

    可能是由于机器无法成功安装.NET 4.0的缘故

  5. Shell之数学计算

    本博客已经迁往http://www.kemaswill.com/, 博客园这边也会继续更新, 欢迎关注~ 数学计算是Shell中比较常用的一种操作,  但是因为shell中所有的变量都默认为字符串, ...

  6. 指定的参数已超出有效值的范围 参数名: utcDate WebResource异常

    指定的参数已超出有效值的范围.参数名: utcDate 说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息.  异常详细信息 ...

  7. Docker练习例子:基于 VNCServer + noVNC 构建 Docker 桌面系统

    0. 安装docker 这一步略,网上有好多教程,一般出现装不上的原因,也就是网速问题了,这个我也很难帮你. 1. 下载指定的镜像images docker pull dorowu/ubuntu-de ...

  8. MyBatis知多少(1)

    SQL (Structured Query Language,结构化查询语言)己经存在很长一段时间了.自从第一次提出“数据可以被规范化为一组相互关联的表”这样的思想以来,已经超过35年了. 从那时起, ...

  9. swift也开源了.

    swift也开源了 微软好多也开源. 这世界都开源了 你还等啥. 是因为 B2D 模式吗. 开发者人数众多, 足可以养活一个公司了.  有的叫生态圈.

  10. Linux高级编程--04.GDB调试程序(设置断点)

    调试已运行的程序 在UNIX下用ps查看正在运行的程序的PID(进程ID),然后用gdb PID格式挂接正在运行的程序. 先用gdb 关联上源代码,并进行gdb,在gdb中用attach命令来挂接进程 ...