Java多线程——synchronized机制
Java多线程——synchronized(内建锁)机制
一、synchronized对象锁相关概念
学习synchronized机制前先提出几个问题
为什么要使用synchronized进行同步(加锁)操作?
synchronized的应用场景及注意事项是什么?
1、为什么要使用synchronized进行同步(加锁)操作:
多个线程对同一个对象中的实例变量(所有线程共有的)进行并发访问时,会产生“脏读”,取到的数据是被更改过的。因此使用synchronized进行同步操作,避免”脏读”的出现,保证线程安全。
2、synchronized的应用场景与注意事项:
①只有共享资源
的读写访问才需要同步化
,如果不是共享资源
,没有同步的必要
。(多个线程有需要共享的东西时,才需要加锁;没有需要共享的东西(每个线程都拥有),就不需要加锁)
②多个线程只是在分别调用线程类的run()方法,其他方法或属性是共享的。
通过进一步的学习提出以下问题
方法中的变量(局部变量)与实例变量是否线程安全?
3、方法中的变量(局部变量)与实例变量的线程安全:
由于局部变量与实例变量的创建时机,局部变量是在被某个线程调用该方法时才会在该线程中为方法中的变量申请空间,因此,局部变量是每个线程所独有的,因此局部变量是线程安全的。而实例变量是在对象创建时就会开辟空间创建,实例变量是所有线程共享的,因此实例变量是线程不安全的。详情请见:局部变量与实例变量的线程安全。
下面就伴随着问题的产生了解synchronized对象锁的概念。
synchronized锁住的到底是什么东西?
synchronized锁住对象后对其他同步/非同步方法(代码块)的影响?
异常对synchronized锁的影响?
什么是”脏读”?
3、synchronized对象锁
的概念:
①防止多个线程同一时刻执行同一个对象的同步段
。
一定要注意是:
多个线程;
同一时刻;
同一对象;
同步段;
②synchronized对象锁的是括号中的对象而非代码
。其他线程需要等待当前线程执行完成后释放锁才能再次给该对象加锁。
③若线程A持有了Object对象的锁
(通过同步方法/同步代码块获取),线程B不能再访问
Object对象的同步方法/同步代码块
,但完全可以异步调用非同步方法/块
。
④若一个线程执行的代码出现异常,其所持有的锁就会自动释放。(抛出异常会释放锁
)
⑤脏读的深入理解
二、synchronized机制相关实现
1、对象锁与全局锁
①对象锁:synchronized(object)、synchronized(this)、以及普通的synchronized方法,锁的是括号中的对象
而非代码。
public synchronized void method(){}
synchronized(Object){}
synchronized(this){}
对象锁中:即便这个对象的某些属性发生改变,但只要对象没有改变,不同线程之间还是同步状态。
②全局锁:使用类的静态同步方法(static synchronized),或在同步代码块中锁当前的Class对象
,此时锁的是当前使用的类(代码段)
而非对象(其实锁住的还是对象
,只不过是Class对象,而非该对象本身)
public static synchronized void method(){}
synchronized(类名称.class){}
2、同步方法与同步代码块
同步代码块与同步方法到底是什么?
synchronized(任意对象)详解
synchronized(任意对象)中为什么不推荐使用含常量池的对象?
同步代码块与同步方法的作用是什么?
为什么多推荐使用同步代码块而不是同步方法?
①同步代码块与同步方法相关概念:
同步代码块:在方法中使用synchronize(对象){},表示同一时刻只能有一个线程能够进入同步代码块
,但是多个线程可以同时进入方法
。
同步代码块分类:
a)synchronized(this){}—该类的对象
b)synchronized(类名.class)-全局锁{}—类反射对象
c)synchronized(任意对象,obj);—任意对象都有markword字段
同步方法:在方法声明上加synchronize,表示此时同一时刻只能有一个线程能够进入同步方法
。
同步方法分类
a)普通方法 //相当于 this
b)静态方法-全局锁 //相当于 类名.class
② synchronized(任意对象)同步代码块详解
此时的任意对象为非this对象
,将该”任意对象”作为”对象监视器”实现同步的功能。
a)当多个线程持有的”对象监视器”为同一个对象
时,synchronized(x){}同步代码块时呈同步效果
。(同一时间只有一个线程可以执行synchronized(x)同步代码块中的代码),若不是同一个对象就是异步执行了。
b)当其他线程执行x对象中synchronized同步方法时呈同步效果(x对象本身也有同步方法)
c)当其他线程执行x对象方法里面的synchronized(this)代码块时也呈同步效果
d)当其他线程调用不加synchronized关键字的普通方法时,还是异步调用。
使用任意对象(非this)的好处
在于:若一个类中有很多个同步方法,这时虽然能实现同步,但多个线程在调用不同synchronized方法时会出现阻塞,而synchronized(非this对象)只要括号内不是同一个对象,执行就是异步的(eg:不会与其他this同步方法争抢this锁),大大提高运行效率。
③ 由于Java中常量池的存在,一般synchronized(任意对象)中的任意对象不使用存在常量池的对象。
eg:由于String存在常量池,所以一般synchronized(x)中x不用String对象。
eg:
print中有以String(传入的参数)为锁的代码块
线程1:service.print("AA")
线程2:service.print("AA")
解析:由于”AA”是匿名对象,在不同的线程中相当于new了一个新的对象,相当于不同的线程拥有不同的对象,按道理这两个线程锁了两个不同的对象,这两个线程之间是异步的,但由于String常量池的存在,这两个对象在内部实际是一个对象,则导致线程2不能访问代码块了。
还没有评论,来说两句吧...