Java 并发编程·synchronized 关键字
synchronized 关键字
synchronized
关键字采用对代码块/方法体加锁的方式解决 Java 中多线程访问同一个资源时,引起的资源冲突问题。
一句话总结:synchronized
能够保证同一时刻最多只有一个线程执行某段代码,以达到保证并发安全的效果。
使用方式
synchronized
同步锁分对象锁和类锁:
- 对象锁:对单个实例对象的独享内存的部分区域加锁
- 类锁:对整个类的共享内存的部分区域加锁
synchronized
关键字最主要的三种使用方式:
- 修饰实例方法
- 修饰静态方法
- 修饰代码块
对象锁,修饰代码块/实例方法
类锁,修饰代码块/静态方法
synchronized
是原子操作,原子操作不可分。
一些栗子
0x01 丢失的请求数
0x02 脏读
业务写方法加锁,读方法不加锁,产生脏读问题:
0x03 同步与非同步方法同时调用
0x04 同步方法调用
一个同步方法可以调用另一个同步方法,一个线程已经拥有了某个对象的锁,再次申请的时候仍然会得到该对象的锁,即 synchronied
同步锁可重入,可重入的粒度是线程级。
可重入的好处:
- 避免死锁
- 提升封装性,避免重复的加锁和释放锁
synchronied
的两大特性:可重入和不可中断。
0x05 子类调用父类同步锁
下面栗子中子类和父类的锁对象是同一个,是 new TT()
子类对象,因为 synchronized
修饰的方法锁的是 this
对象,而这里 this
就是指向子类对象。
0x06 异常释放锁
程序在执行过程中,如果出现异常,默认情况下锁会被释放。所以,在并发处理过程中,有异常要多加小心,不然可能会发生不一致的情况。
比如第一个线程出现异常,锁被释放,其他线程进入同步代码区,有可能会访问到异常产生时的数据。
语句优化
synchronized 同步代码块中的语句越少越好。比较下面 m1 和 m2。
锁对象改变
锁定某对象 o,如果 o 属性发生变化,不影响锁的使用。但是如果 o 引用了新的一个对象,则锁定的对象发生改变。应该避免将锁定对象的引用变成另外的对象。
避免字符串作为锁对象
不要以字符串常量作为锁对象,在下面栗子中,m1 和 m2 其实锁定的是同一个对象。
面试题
0x01 元素监听
实现一个容器,提供两个方法 add 和 size。写两个线程,线程 1 添加 10 个元素到容器中,线程 2 实现监控元素的个数,当个数到 5 时,线程 2 给出提示并结束。
初步实现:
上面方法有两个缺点,一是循环判断浪费 cpu,二是判断时依旧可能会新增元素,不够精确。
进阶优化版:
需要注意的是 wait
会释放锁,而 notify
不会释放锁。需要让 t2 监听线程先执行,然后等待,t1 线程添加元素到 5 个时通知 t2,同时 t1 自己 wait
释放锁,不然 t2 无法执行,等到 t2 执行完毕,再通知 t1 执行。t1、t2 线程交替执行,通信过程比较繁琐。
最终优化版,使用门闩 CountDownLatch
代替 wait
和 notify
,CountDownLatch
不涉及到锁定:
当不涉及同步,只是涉及线程通信的时候,用 synchorized + wait/notify
显得太重了。
金点网络-全网资源,一网打尽 » Java 并发编程·synchronized 关键字
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。
- 是否提供免费更新服务?
- 持续更新,永久免费
- 是否经过安全检测?
- 安全无毒,放心食用