前言
synchronized可以为任意对象加锁,用法比较灵活,语法如下
(1)修饰代码块,作用于调用的对象;
(2)修饰方法,作用于调用的对象;
(3)修饰静态方法,作用于所有对象;
(4)修饰类,作用于所有对象。
synchronized取得的锁都是对象锁,而不是把一段代码(或者方法)当成锁!
使用synchronized时,应该注意以下细节问题:
1、synchronized锁的重入性
如下列代码所示,类似于ReentrantLock
在method1中没有释放锁的情况下,可以继续调用synchronized修饰的method2
public class SyncDubbo { public synchronized void method1(){ System.out.println("method1..."); method2(); } public synchronized void method2(){ System.out.println("method2..."); method3(); } public synchronized void method3(){ System.out.println("method3..."); } public static void main(String[] args) { final SyncDubbo sd = new SyncDubbo(); Thread t1 = new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub sd.method1(); } }, "t1"); t1.start(); } }
如下列代码所示,在子类与父类之间相互调用也运用了synchronized的重用性
public class FatherSon { static class Father { public int num = 10; public synchronized void method1(){ try { num --; System.out.println("Father num = " + num); Thread.sleep(100); } catch (Exception e) { // TODO: handle exception } } } static class Son extends Father { public synchronized void method2(){ try { while (num >0) { num --; System.out.println("Son num = " + num); Thread.sleep(100); this.method1(); } } catch (Exception e) { // TODO: handle exception } } } public static void main(String[] args) { Thread t1 = new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub Son son = new Son(); son.method2(); } }, "t1"); t1.start(); } }
2、不要使用字符串常量作为锁
如下列代码所示,使用了字符串常量作为锁,那么t1和t2运行之后将会一直在t1中出现死循环,t2永远拿不到锁!
解决:可以使用 new String("字符串常量") 作为锁
public class StringLock { public void method(){ synchronized("字符串常量"){ try { while (true){ System.out.println("当前线程:" + Thread.currentThread().getName() + "开始"); Thread.sleep(1000); System.out.println("当前线程:" + Thread.currentThread().getName() + "结束"); } } catch (Exception e) { // TODO: handle exception } } } public static void main(String[] args) { final StringLock sl = new StringLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub sl.method(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub sl.method(); } }, "t2"); t1.start(); t2.start(); } }
3、锁对象的改变问题
如下列代码所示,使用synchronized锁住了一个对象,并且在代码里重新new了一个对象,导致锁对象改变。
这样在t1还没有运行完代码的时候,t2就已经可以拿到锁了,显然会出现问题!
解决:不要改变锁对象(但是改变对象的属性对代码不会有影响,比如锁是一个人,那么可以改变这个人的身高、年龄等)
public class ChangeLock { Object obj = new Object(); public void method(){ synchronized(obj){ try { System.out.println("当前线程:" + Thread.currentThread().getName() + "开始"); //从这里改变锁对象 obj = new Object(); Thread.sleep(3000); System.out.println("当前线程:" + Thread.currentThread().getName() + "结束"); } catch (Exception e) { // TODO: handle exception } } } public static void main(String[] args) { final ChangeLock cl = new ChangeLock(); Thread t1 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub cl.method(); } }, "t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub cl.method(); } }, "t2"); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t2.start(); } }