通过不同的情景来理解锁
-
情景1
public class LockDetail { public static void main(String[] args) throws InterruptedException { Print print = new Print(); new Thread(()->{ print.printFun1(); }).start(); //两个线程之间休眠一秒 TimeUnit.SECONDS.sleep(1); new Thread(()->{ print.printFun2(); }).start(); } } class Print{ public synchronized void printFun1(){ try { //休眠3秒 TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("printFun1"); } public synchronized void printFun2(){ System.out.println("printFun2"); } }
不管怎么执行 上面的打印结果都是
printFun1 printFun2
原因:
printFun1和printFun2都是成员方法,
在成员方法上加的锁,其实是对调用对象加了锁
现在只有print这一个对象,两个线程都在尝试获取这个对象锁。而Thread1先获得锁,所以先打印printFun1 -
情景2。再加一个对象。两个线程分别调用两个对象方法
public class LockDetail { public static void main(String[] args) throws InterruptedException { Print print1 = new Print(); Print print2 = new Print(); new Thread(()->{ print1.printFun1(); }).start(); //两个线程之间休眠一秒 TimeUnit.SECONDS.sleep(1); new Thread(()->{ print2.printFun2(); }).start(); } } class Print{ public synchronized void printFun1(){ try { //休眠3秒 TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("printFun1"); } public synchronized void printFun2(){ System.out.println("printFun2"); } }
不管怎么执行,都会打印
printFun2 printFun1
原因分析:
两个线程获取的不同的对象的锁。
thread1获取print1的锁,thread2获取print2的锁。
因为printFun1方法会休眠3秒,所以先打印printFun2
-
情景3。把成员方法变成静态方法
public class LockDetail { public static void main(String[] args) throws InterruptedException { Print print = new Print(); new Thread(()->{ print.printFun1(); }).start(); //两个线程之间休眠一秒 TimeUnit.SECONDS.sleep(1); new Thread(()->{ print.printFun2(); }).start(); } } class Print{ public static synchronized void printFun1(){ try { //休眠3秒 TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("printFun1"); } public static synchronized void printFun2(){ System.out.println("printFun2"); } }
这时候打印的是
printFun1 printFun2
原因分析:
虽然和情景1打印的内容相同,但是原因不一样
printFun1和printFun2此时都变成了静态方法。这两个方法都属于类。
虽然在线程中是通过类对象来访问的,其实和使用类直接调用是一样的。Print.printFun1,Print.printFun1。
这个时候在线程中获取的是Print.class这个对象的锁。而这个对象是全局唯一的。
线程1先拿到锁,所以先打印printFun1。
-
情景4。再添加一个对象
public class LockDetail { public static void main(String[] args) throws InterruptedException { Print print1 = new Print(); Print print2 = new Print(); new Thread(()->{ print1.printFun1(); }).start(); //两个线程之间休眠一秒 TimeUnit.SECONDS.sleep(1); new Thread(()->{ print2.printFun2(); }).start(); } } class Print{ public static synchronized void printFun1(){ try { //休眠3秒 TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("printFun1"); } public static synchronized void printFun2(){ System.out.println("printFun2"); } }
打印结果和情景3一样。
因为不管是通过多少个对象来调用。在静态方法上调用的始终是Print.class这个对象的锁。或者叫类锁。
-
同步代码块和上面的情形是一样的
//对象锁 public void printFun2(){ synchronized (this){ System.out.println("printFun2"); } } //类锁 public void printFun2(){ synchronized (Print.class){ System.out.println("printFun2"); } } //类锁 public static void printFun2(){ synchronized (Print.class){ System.out.println("printFun2"); } }
-
总结
java中的锁其实只有一种锁,都是对象锁。
区别于一个是自定义的类对象,一个是Class类的对象。
自定义类的对象可以有多个,而Class类的对象全局唯一。