synchronized

synchronized的几种表现形式

 1.对于普通同步方法,锁的是当前对象实例。

 1 public synchronized void minus() {
 2         int count = 5;
 3         for (int i = 0; i < 5; i++) {
 4             count--;
 5             System.out.println(Thread.currentThread().getName() + " - " + count);
 6             try {
 7                 Thread.sleep(500);
 8             } catch (InterruptedException e) {
 9             }
10         }
11     }

 

 2.对于静态同步方法,锁的是当前类的class对象。

 1  public static synchronized void minus5() {
 2         int count = 5;
 3         for (int i = 0; i < 5; i++) {
 4             count--;
 5             System.out.println(Thread.currentThread().getName() + " - " + count);
 6             try {
 7                 Thread.sleep(500);
 8             } catch (InterruptedException e) {
 9             }
10         }
11     }

 

 3.对于同步方法块,锁的是括号配置的对象。

    注意:如果synchronized括号内是int1、string1,则输出4 3 2 1 0 4 3 2 1 0(因为两个类实例锁的是同一个变量,如果private final Integer int1=new Integer(1); 则输出 4 4 3 3 2 2 1 1 0 0 );

              如果是object1锁的的是当前object类实例,则输出4 4 3 3 2 2 1 1 0 0;

              如果括号内是静态实例,则锁的是jvm内唯一变量,因为输出4 3 2 1 0 4 3 2 1 0

 1 private final Integer int1=0;
 2 private final String string1="1";
 3 private final Object object1=new Object();
 4 private static final Object object2=new Object();
 5 
 6 public void minus4() {
 7 
 8     synchronized(object2){
 9 
10         int count = 5;
11         for (int i = 0; i < 5; i++) {
12             count--;
13             System.out.println(Thread.currentThread().getName() + " - " + count);
14             try {
15                 Thread.sleep(500);
16             } catch (InterruptedException e) {
17             }
18         }
19     }
20 }

 

synchronized的继承关系

 synchronized不能被继承。

 synchronized是非函数签名,因此无法被继承,所有无法保证子类调用同步。

 子类继承父类,不重写父类的synchronized方法,则调用的父类的方法,保证同步;重写后调用子类的非同步方法,不保证同步。

 

synchronized的原理

  synchronized是一个重量级锁,实现依赖于JVM的monitor监视器锁。

  主要使用monitorenter和monitorexit指令来实现方法同步和代码块同步。

  在编译的时候,会将monitorenter指令插入到同步代码块的其实位置,monitorexit插入到方法结束处和异常处,并且每一个monitorenter都有一个对应的monitorexit。

  任何对象都有一个monitor与之关联,当monitor被劫持后,他将处于锁定状态,线程执行到monitorenter指令时,会尝试获取对象所对应的monitor的所有权,即获取对象锁的锁。当方法执行完毕或出现异常会自动释放锁。

 

synchronized的特性

  而且synchronized具有重入性,即在线程同一锁中,线程不需要再次获取同一把锁。

  每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加1,释放锁就会将计数器减1。

  具有可见性,线程A将变量a从0赋值到1,线程b在获取锁时,会从主内存将获取到最新值。

  

 

 如何优化synchronized这个重量级锁

  为了减少获取锁和释放锁带来的性能损耗,引入了偏向锁、轻量级锁、重量级锁来进行优化。

  首先是一个无锁状态,当线程进入同步代码块的时候,会检查对象头和栈帧中的锁记录里是否存入当前线程的ID,如果没有使用CAS进行替换。

  以后该线程进入和退出同步代码块不需要进行CAS操作来加锁和解锁,只需要判断对象头的Mark word内是否存储指向当前线程的偏向锁。

  如果没有或者不是,则需要使用CAS进行替换,如果设置成功则当前线程持有偏向锁,反之将偏向锁进行撤销并升级为轻量级锁。

  轻量级锁加锁过程,线程在执行同步块之前,JVM会在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头的Mark Word复制到锁记录(Displaced Mark Word)中,然后线程尝试使用CAS 将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得    锁,反之表示其他线程竞争锁,当前线程便尝试使用自旋来获得锁。

  轻量级锁解锁过程,解锁时,会使用CAS将Displaced Mark Word替换回到对象头,如果成功,则表示竞争没有发生,反之则表示当前锁存在竞争锁就会膨胀成重量级锁。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
posted @ 2020-10-27 20:42  蹦蹦郭  阅读(133)  评论(0编辑  收藏  举报