Synchronized的两种用法
-
Synchronized作用:能够保证在同一时刻最多只有一个线程执行该代码,以达到保证并发安全的效果
-
对象锁
- 方法锁:Synchronized修饰普通方法,默认锁对象为this当前实例对象
- 同步代码块锁:手动指定锁对象
-
类锁
- Synchronized修饰静态的方法
- 指定锁为Class对象,类锁只能在同一时刻被一个对象拥有
多线程访问同步方法的7种情况
-
两个线程同时访问一个对象的同步方法
争抢同一把锁,相互等待
public class SynchronizedObjectMethod3 implements Runnable { static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3(); public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("finished"); } @Override public void run() { method(); } public synchronized void method(){ System.out.println("对象锁的普通方法形式:" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } }
-
两个线程访问的是两个对象的同步方法
争抢不同的锁,并发执行
public class SynchronizedObjectCodeBlock2 implements Runnable { static SynchronizedObjectCodeBlock2 instance1 = new SynchronizedObjectCodeBlock2(); static SynchronizedObjectCodeBlock2 instance2 = new SynchronizedObjectCodeBlock2(); Object lock1 = new Object(); Object lock2 = new Object(); @Override public void run() { synchronized (this) { System.out.println("对象锁的代码块形式:"+ Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":lock1运行结束"); } } public static void main(String[] args) { Thread t1 = new Thread(instance1); Thread t2 = new Thread(instance2); t1.start(); t2.start(); while(t1.isAlive() || t2.isAlive()){ } System.out.println("finished"); } }
-
两个线程访问的是synchronized的静态方法
争抢同一把锁,相互等待
public class SynchronizedClassStatic4 implements Runnable { static SynchronizedClassStatic4 instance1 = new SynchronizedClassStatic4(); static SynchronizedClassStatic4 instance2 = new SynchronizedClassStatic4(); @Override public void run() { method(); } public synchronized static void method(){ System.out.println("类锁的静态方法形式:" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread t1 = new Thread(instance1); Thread t2 = new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("finished"); } }
-
同时访问同步方法与非同步方法
非同步方法不受影响,同步方法与非同步方法并发执行
public class SynchronizedYesAndNo6 implements Runnable { static SynchronizedYesAndNo6 instance = new SynchronizedYesAndNo6(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ method1(); } else { method2(); } } public synchronized void method1(){ System.out.println("加锁的方法:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public void method2(){ System.out.println("未加锁的方法:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("finished"); } }
-
访问同一个对象的不同的普通同步方法
相互等待,默认为this对象,争抢同一把锁
public class SynchronizedDifferentMethod7 implements Runnable { static SynchronizedDifferentMethod7 instance = new SynchronizedDifferentMethod7(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ method1(); } else { method2(); } } public synchronized void method1(){ System.out.println("加锁的方法1:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public synchronized void method2(){ System.out.println("加锁的方法2:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("finished"); } }
-
同时访问静态synchronized和非静态synchronized方法
并发执行,静态方法使用类锁,非静态方法使用对象锁
public class SynchronizedStaticAndNormal8 implements Runnable { static SynchronizedStaticAndNormal8 instance = new SynchronizedStaticAndNormal8(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ method1(); } else { method2(); } } public synchronized static void method1(){ System.out.println("加锁的静态方法:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public synchronized void method2(){ System.out.println("加锁的方法普通:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("finished"); } }
-
方法抛异常后,会释放锁
一个同步方法抛异常后,jvm自动释放锁;锁可以被下一个同步方法获取到
public class SynchronizedException9 implements Runnable { static SynchronizedException9 instance = new SynchronizedException9(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ method1(); } else { method2(); } } public synchronized void method1(){ System.out.println("加锁的方法1:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } throw new RuntimeException(); } public synchronized void method2(){ System.out.println("加锁的方法2:" + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("finished"); } }
Synchronized的性质
- 可重入:同一线程的外层函数获得锁之后,内层函数可以直接再次获取该锁;避免死锁,提高封装性;粒度是线程而非调用
-
同一个方法是可重入的
public class SynchronizedRecursion10 { int i = 0; public static void main(String[] args) { SynchronizedRecursion10 instance = new SynchronizedRecursion10(); instance.method1(); } private synchronized void method1(){ System.out.println("method1: " + i); if (i == 0){ i++; method1(); } } }
-
可重入不要求是同一个方法
public class SynchronizedOtherMethod11 { private synchronized void method1(){ System.out.println("method1"); method2(); } private synchronized void method2(){ System.out.println("method2"); } public static void main(String[] args) { SynchronizedOtherMethod11 s = new SynchronizedOtherMethod11(); s.method1(); } }
-
可重入不要求是同一个类中的
public class SynchronizedSuperClass12 { public synchronized void doSomething(){ System.out.println("父类方法"); } } class TestClass extends SynchronizedSuperClass12{ @Override public synchronized void doSomething(){ System.out.println("子类方法"); super.doSomething(); } public static void main(String[] args) { TestClass t = new TestClass(); t.doSomething(); } }
-
- 不可中断:一旦锁被其他线程获得,只能选择等待或者阻塞,直到别的线程释放锁
Synchronized的原理
-
加锁和释放锁的原理
synchronized会自动解锁,而Lock要手动解锁,效果一样
public class SynchronizedToLock13 { Lock lock = new ReentrantLock(); public synchronized void method1(){ System.out.println("Synchronized形式的锁"); } public void method2(){ lock.lock(); try { System.out.println("lock形式的锁"); } finally { lock.unlock(); } } public static void main(String[] args) { SynchronizedToLock13 s = new SynchronizedToLock13(); s.method1(); s.method2(); } }
-
可重入性原理
JVM
负责跟踪对象被加锁的次数monitorenter
表示加锁,monitorexit
表示解锁- 线程第一次给对象加锁的时候,计数变为1。每当这个相同的线程在此基础上再次获得锁时,计数会递增
- 每当任务离开时,计数递减,当计数为0时,锁完全被释放
public class Decompilation14 { private Object object = new Object(); public void insert(Thread thread){ synchronized (object){ } } public static void main(String[] args) { Decompilation14 decompilation14 = new Decompilation14(); } }
-
可见性原理