Synchronized解析(一)

tech2022-07-30  165

基本使用

Synchronized关键字是jdk内置的解决并发问题的锁。jdk1.6以后进行了优化,增加了偏向锁、轻量锁、重量锁。用法:

修饰普通方法修饰静态方法修饰代码块

举例:

修饰普通方法

public class SychMethod { public synchronized void method1(){ System.out.println("sych method1 start"); try { System.out.println("sych method1 running"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sych method1 end"); } public synchronized void method2(){ System.out.println("sych method2 start"); try { System.out.println("sych method2 running"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sych method2 end"); } }

修饰静态方法

public class SychStatic { public static synchronized void method1(){ System.out.println("sych static method1 start"); try { System.out.println("sych static method1 running"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sych static method1 end"); } public static synchronized void method2(){ System.out.println("sych static method2 start"); try { System.out.println("sych static method2 running"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("sych static method2 end"); } }

修饰代码块

public class SychObject { public void method1(){ try { System.out.println("sych object method1 start"); synchronized (this){ System.out.println("sych object method1 running"); Thread.sleep(3000); } System.out.println("sych object method1 end"); } catch (InterruptedException e) { e.printStackTrace(); } } public void method2(){ try { System.out.println("sych object method2 start"); synchronized (this){ System.out.println("sych object method2 running"); Thread.sleep(1000); } System.out.println("sych object method2 end"); } catch (InterruptedException e) { e.printStackTrace(); } } }

测试代码

public class SychTest { @Test public void test1() throws InterruptedException { //synchronized 修饰方法,锁的是sychMethod对象实例上的monitor SychMethod sychMethod = new SychMethod(); ExecutorService exec = Executors.newCachedThreadPool(); exec.execute(new Runnable() { @Override public void run() { sychMethod.method1(); } }); exec.execute(new Runnable() { @Override public void run() { sychMethod.method2(); } }); Thread.sleep(5000); //synchronized 修饰对象实例,锁的是this实例对象的monitor SychObject sychObject1 = new SychObject(); exec.execute(new Runnable() { @Override public void run() { sychObject1.method1(); } }); exec.execute(new Runnable() { @Override public void run() { sychObject1.method2(); } }); Thread.sleep(5000); //synchronized 修饰静态方法,锁的是class类对象 exec.execute(new Runnable() { @Override public void run() { SychStatic.method1(); } }); exec.execute(new Runnable() { @Override public void run() { SychStatic.method2(); } }); Thread.sleep(5000); } }

测试结果:

sych method1 start sych method1 running sych method1 end sych method2 start sych method2 running sych method2 end

=====================

sych object method1 start sych object method1 running sych object method2 start sych object method1 end sych object method2 running sych object method2 end

=====================

sych static method1 start sych static method1 running sych static method1 end sych static method2 start sych static method2 running sych static method2 end

从测试结果就可以看出Synchronized在修饰方法、静态方法、代码块都保证了线程之间的互斥性,保证了并发的安全性。

从反编译看实现原理

对修饰代码块进行反编译:

public demo.think.currenttest.sych.SychObject();

public void method1(); Code: 9: dup 10: astore_1 11: monitorenter … 27: monitorexit 28: goto 36 33: monitorexit

monitorenter和monitorexit这两条指令完成了加锁和解锁

monitorenter:

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: • If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. • If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. • If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

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

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

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

monitorexit:

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

执行monitorexit的线程必须是objectref所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

对修饰方法进行反编译:

public static synchronized void method1(); descriptor: ()V flags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED

从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

转自:https://www.cnblogs.com/paddix/p/5367116.html

最新回复(0)