一、对象方法锁
1、成员方法加锁
同一个对象成员方法有3个synchronized修饰的方法,通过睡眠模拟业务操作
public class CaseOne {public synchronized void m1(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+"------------>m1");}public synchronized void m2() {try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+"------------>m2");}public synchronized void m3() {try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+"------------>m3");}}
private final static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");private final static ExecutorService POOL = Executors.newFixedThreadPool(4);public static void main(String[] args) {CaseOne caseOne = new CaseOne();System.out.println(SDF.format(new Date())+"------------>start");POOL.execute(caseOne::m1);POOL.execute(caseOne::m2);POOL.execute(caseOne::m3);}
运行结果:
通过运行结果可以看出,三个业务方法,执行完成总共花费了6s,虽然使用了多线程,这三三个方法其实是串行作业的,因此可得出一下结论:
同一个对象的不同方法加synchronized修饰,只要其中一个方法抢到锁,其他被synchronized修饰的方法都互斥,本质是对象上锁。
2、成员方法不加锁
public class CaseTwo {public synchronized void m1(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+"------------>m1");}public void m2() {try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+"------------>m2");}public synchronized void m3() {try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+"------------>m3");}}
CaseTwo caseTwo = new CaseTwo();System.out.println(SDF.format(new Date())+"------------>start");POOL.execute(caseTwo::m1);POOL.execute(caseTwo::m2);POOL.execute(caseTwo::m3);
运行结果:
三个业务方法执行完成总共花费了4s,结合第一个案例可以得出以下结论:
同一个对象的不同方法加synchronized,其中加锁方法和不加锁的成员方法没有竞争关系,不产生互斥。
3、不同对象方法锁
案例1和案例2研究的是同一个的对象,加锁和不加锁的情况,下面来研究不同对象的情况。
public static class CaseThree {private final String name;public CaseThree(String name) {this.name = name;}public synchronized void m1(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+ " "+this.name +"------------>m1");}}
CaseThree caseThree1 = new CaseThree("caseThree1");CaseThree caseThree2 = new CaseThree("caseThree2");System.out.println(SDF.format(new Date())+" "+"------------>start");POOL.execute(caseThree1::m1);POOL.execute(caseThree2::m1);
运行结果:
由执行结果可以看出,对象caseThree1 和对象caseThree12调用同一个方法,同时执行完成,可以得出以下结论:
不同的对象执行同一个成员方法,没有竞争关系,加锁方法不互斥。
二、类方法加锁
下面研究一下一个类型static修饰的静态方法加锁
1、静态方法锁
public static class CaseFour {public static synchronized void m1(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+ " "+"------------>m1");}public static synchronized void m2(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+ " "+"------------>m2");}}
System.out.println(SDF.format(new Date())+" "+"------------>start");POOL.execute(CaseFour::m1);POOL.execute(CaseFour::m2);
由执行的结果可以得出以下结论:
同一个类的不同静态方法之间存在竞争关系,先抢到锁的先执行。
2、不同对象静态方法锁
public static class CaseFive {public static synchronized void m1(String name){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+ " "+name+"------------>m1");}}
CaseFive caseFive1 = new CaseFive();CaseFive caseFive2 = new CaseFive();System.out.println(SDF.format(new Date())+" "+"------------>start");POOL.execute(()->caseFive1.m1("caseFive1"));POOL.execute(()->caseFive2.m1("caseFive2"));
由运行结果可以得到以下结论:
同一个类的不同对象调用同一个静态方法,存在竞争关系,会产生互斥。
3、类方法锁和成员方法锁
◆场景一:
public static class CaseSix {public static synchronized void m1(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+ " "+"------------>m1");}public synchronized void m2(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace();}System.out.println(SDF.format(new Date())+ " "+"------------>m2");}}
CaseSix caseSix = new CaseSix();System.out.println(SDF.format(new Date())+" "+"------------>start");POOL.execute(()->caseSix.m1());POOL.execute(()->caseSix.m2());
由执行结果可得出以下结论:
同一个类的静态方法和成员方法加锁,同一个对象同时调用,会场生竞争关系,先抢到锁的先执行。
◆场景二:
CaseSix caseSix1 = new CaseSix();CaseSix caseSix2 = new CaseSix();System.out.println(SDF.format(new Date())+" "+"------------>start");POOL.execute(()->caseSix1.m1());POOL.execute(()->caseSix2.m2());
由运行结果可知:
同一个类的不同对象,分别调用静态类和成员方法,不产生竞态关系。
三、线程锁总结
类方法做是在类加载过程中已经打上标记了,类信息存储在jvm的常量池中,而对象的方法锁是在运行动态确定的,因此类方法锁和不同的对象成员方法锁之间不存在竞争关系。在并发情景,能用无锁的数据块就不要用锁,能锁区块,就不要锁整个方法体,能锁对象就不要用类锁。