1、实例锁
1.1、代码
public class SynchronizedTest {
synchronized void demo1(){
//等价于synchronized(this) 是对这个类的实例进行加锁
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+DateTimeUtils.getCurrDateTime());
}
//与demo1等价
void demo2(){
synchronized (this){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+DateTimeUtils.getCurrDateTime());
}
}
public static void main(String[] args) {
SynchronizedTest test1=new SynchronizedTest();
//实例锁
new Thread(()->test1.demo1(),"demo1").start();
new Thread(()->test1.demo1(),"demo2").start();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
SynchronizedTest test2=new SynchronizedTest();
SynchronizedTest test3=new SynchronizedTest();
new Thread(()->test2.demo1(),"demo3").start();
new Thread(()->test3.demo1(),"demo4").start();
}
}
1.2、执行结果
1.3、结果分析
- demo1和demo2线程都是对test1实例进行抢占锁,存在互斥性,所以存在阻塞
- demo3和demo4是两个实例 test2和test3,所以不存在阻塞
- 只针对当前对象实例加锁,test1、test2、test3存在三个锁,因为这是三个实例
2、静态方法=类锁=类对象锁
2.1、代码
public class SynchronizedTest {
synchronized static void demo1(){
//等价于synchronized(this) 是对这个类的实例进行加锁
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+DateTimeUtils.getCurrDateTime());
}
//与demo1等价
void demo2(){
synchronized (SynchronizedTest.class){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+DateTimeUtils.getCurrDateTime());
}
}
public static void main(String[] args) {
SynchronizedTest test1=new SynchronizedTest();
//类锁
new Thread(()->test1.demo1(),"demo1").start();
new Thread(()->test1.demo1(),"demo2").start();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
SynchronizedTest test2=new SynchronizedTest();
SynchronizedTest test3=new SynchronizedTest();
//类锁
new Thread(()->test2.demo1(),"demo3").start();
new Thread(()->test3.demo1(),"demo4").start();
}
}
2.2、运行结果
2.3、结果分析
- 加在类上的锁,不管是不是一个实例,都会存在互斥性,进行锁竞争
- 类锁和对象锁 体现在互斥的范围
- 类锁:针对所有的对象都互斥
3、代码块锁
其实就是锁对象的应用范围
4、锁升级
4.1、 互斥锁的本质-> 共享资源
4.2、锁升级过程:由于加锁一定会带来性能开销,所以JDK1.6后做了锁优化,锁逐渐升级,ThreadA持有了偏向锁,ThreadB来抢占偏向锁,若在偏向锁内部的处理机制中,ThreadB不能抢占成功获得锁,则会升级到轻量级锁,如果轻量级锁中还没有获取到,则ThreadB在重量级锁中进行阻塞;
4.2.1、优化的目的?
在ThreadB线程阻塞前,获得锁;在不加锁的情况下解决线程安全问题
4.2.2、性能开销?
是线程的阻塞带来性能开销
4.3、偏向锁过程
- 线程1进入代码块后,获得偏向锁,并且线程1将同步块的互斥标记修改
- 在有线程1获得偏向锁的阶段,线程2访问代码块,会先检查对象头是否存储里线程2,如果没有则尝试CAS替换Mark-Word,如果替换不成功,则撤销原有偏向锁,然后进行锁升级(轻量级锁);如果替换成功则线程2获得偏向锁
2.1、扩展:
什么时候CAS替换成功,由线程2获得偏向锁呢?
答:
1》线程1已经执行结束
2》已经升级到轻量级锁+
3》已经是全局安全点,没有指令在操作
什么时候开启轻量级锁?
答:
1》如果现成本身就存在竞争则不要开始轻量级锁->因为轻量级锁本身就是一种优化,目的是提升数据在没有竞争的时候的效率
2》轻量级锁默认是关闭的