博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JUC解析-synchronized
阅读量:5843 次
发布时间:2019-06-18

本文共 2331 字,大约阅读时间需要 7 分钟。

前面主要讲了基于简单的基本类型的原子操作,对于一些复杂的操作就需要用锁来实现互斥,在JUC中定义了一套锁的实现框架,但是在学习前,感觉有必要先回顾了java中自带的锁synchronized。

介绍

synchronized的作用如下

  • 确保线程互斥的访问同步代码(互斥性)。

  • 保证共享变量的修改能够及时可见(可见性)。

  • 有效解决重排序问题(有序性)。

在这里主要讲解下Synchronized在作用1的实现原理以及使用的方式,对于Synchronized主要有三个使用场景:

  • 修饰普通方法

  • 修饰静态方法

  • 修饰代码块

举个栗子:

public class Counter {      private static int count;    private Object lock = new Object();          public synchronized void thisIncr1() {        count ++;    }      public static synchronized void classIncr1() {        count++;    }      public void lockIncr() {            synchronized(lock) {            count++;        }    }      public void thisIncr2(){             synchronized(this) {            count ++;        }    }    public synchronized int getCount() {        return count;    }      public void classIncr2() {            synchronized(Counter.class) {            count++;        }    }}复制代码

如上thisIncr1中修饰的普通方法,classIncr1中修饰的静态方法,thisIncr2和classIncr2修饰的是代码块,上面的同步逻辑有什么区别呢?

synchronized保护的是对象而非代码,只要是访问同一个对象的synchronized方法,即使是不同的代码,也会被同步顺序访问,在上面的代码中thisIncr1和thisIncr2都是同步的this对象。classIncr1和classIncr2同步的Counter.class对象。lockIncr同步的是lock对象。从上面可以确定thisIncr*、classIncr*、lockIncr相互之间是没有互斥同步关系的,只有在他们本身类型之间才会同步执行, 具体可以自行写测试用例测试下。

synchronized的实现原理

举个栗子:

public class SynchronizedDemo {        public void method() {                synchronized (this) {          System.out.println("hello word");        }    }}复制代码

通过javap反编译后为:

通过反编译后的代码,可以看到在synchronized开始和结束的时候分别执行了monitorenter和monitorexit,在java中每个对象有一个监视器锁(monitor),当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

  • 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

  • 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1。

  • 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

从上面分析可以看出,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

synchronized的属性

  • 可重入性

    对同一个执行线程,它在获得了锁之后,在调用其他需要同样锁的代码时,可以直接调用,比如说,在一个synchronized实例方法内,可以直接调用其他synchronized实例方法。可重入是一个非常自然的属性,应该是很容易理解的,之所以强调,是因为并不是所有锁都是可重入的。

  • 内存可见性

    线程在释放锁时,所有写入都会写回内存,而获得锁后,都会从内存中读最新数据。如果仅仅是为了数据的可见性可以用volitile来代替,成本会小很多。

  • 死锁

    使用synchronized或者其他锁,要注意死锁,所谓死锁就是类似这种现象,比如, 有a, b两个线程,a持有锁A,在等待锁B,而b持有锁B,在等待锁A,a,b陷入了互相等待,最后谁都执行不下去,解决的办法就是首先,应该尽量避免在持有一个锁的同时去申请另一个锁,如果确实需要多个锁,所有代码都应该按照相同的顺序去申请锁,顺便说一下,如果程序出现了死锁可以通过pstack命令来查看进程的状态来发现死锁。

转载地址:http://niqcx.baihongyu.com/

你可能感兴趣的文章
tcpdump使用方法总结
查看>>
What is Cluster Aware Updating in Windows Server 2012?
查看>>
Linux命令详解 -- tar
查看>>
Java.net.URL学习总结
查看>>
Tracert(跟踪路由)是路由跟踪实用程序,用于确定 IP 数据包访问目标所采取的路径。...
查看>>
进老男孩的自我介绍和决心书
查看>>
Leetcode Construct Binary Tree from Inorder and Postorder Traversal
查看>>
线上Linux服务器运维安全策略经验分享
查看>>
Django实现组合搜索的方法示例
查看>>
Android一些问题的解决方案
查看>>
Clion 常用快捷键
查看>>
ios之UIToolBar
查看>>
10道典型的JavaScript面试题
查看>>
centos 6.5 docker  安装
查看>>
依然前往
查看>>
C++静态局部对象
查看>>
Hibernate用JPA注解 CascadeType.REMOVE 不起作用的解决办法
查看>>
一步步学习EF Core(3.EF Core2.0路线图)
查看>>
go语言中的坑
查看>>
python之常用模块
查看>>