一. this 锁
同步函数其实用到的锁就是 this 锁,为什么他用到的是 this 锁呢?为了证实这个结论我 们本节将会有两个实验性的程序来作为支撑,说服自己和读者证明同步函数用到的就是 this 锁好了,请看下第一个程序
需求:
我们定义一个类,其中有两个方法,均加了同步锁,假设函数的同步不是 this 锁,我们 如果启动一个线程调用方法 A,另外一个线程调用用方法 B,A 方法和 B 方法里均是死循环, 按照同步函数不是 this 锁的逻辑,两个函数中的逻辑将会被同时执行,但是情况是否是这样, 我们需要通过代码进行实验
public class ClassA { public synchronized void A() { System.out.println("AAAAAAAAAAAAAAAAA"); while (true) { } } public synchronized void B() { System.out.println("BBBBBBBBBBBBBBBBB"); while (true) { } } }
public class MethodSynchronizedTest { public static void main(String[] args) { final ClassA clazz = new ClassA(); // 启动一个线程 new Thread(new Runnable() { public void run() { clazz.A();// 调用A方法 } }).start(); // 启动另一个线程 new Thread(new Runnable() { public void run() { clazz.B();// 调用B方法 } }).start(); } }
分别启动了两个线程,分别用来执行 ClassA 中的两个方法 A 和 B,两个方法都是加了锁 的,也就是说某个线程尽到方法 A 中其他线程就不能进入 A,但是另一个线程应该能进入 B, 但是我们等了半天方法 B 仍然没有输出,因此我们得出一个结论,他们的锁是同一个,至于 是哪一个锁呢?答案就是 this 锁;但是这个例子似乎还不过瘾,那么我们继续修正叫号的程 序,让读者有一个更加直观的理解;
需求: 我们增加一个方法,也用来进行叫号,在 run 方法中也进行叫号,第一个线程调用 run 方法中的逻辑,第二个方法调用函数中的逻辑;
public class TicketWindow3 implements Runnable { private int max_value = 0; private Object lock = new Object(); private boolean flag = true; @Override public void run() { if (flag) { while (true) { //同步函数其实用到的锁就是 this 锁, this锁针对的是对象 //静态锁,锁是类的字节码信息,因此如果一个类的函数为静态方法,那么我们需要通过该类的 class 信息进行加锁; synchronized (lock) { if (max_value > 500) { break; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ":lock..." + max_value++); } } } else { while (true) if (ticket()) break; } } private synchronized boolean ticket() { if (max_value > 500) { return true; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ": method.." + max_value++); return false; } public void change() throws InterruptedException { Thread.sleep(30);// 读者可以自行思考为什么要sleep this.flag = false; } }
public class Bank3 { public static void main(String[] args) throws InterruptedException { TicketWindow3 tw3 = new TicketWindow3(); Thread t1 = new Thread(tw3); Thread t2 = new Thread(tw3); t1.start(); tw3.change(); t2.start(); } }
文字说明: 可能到输出性信息,其中会有 501 这样的信息输出,为什么会这样呢?因为上述的代码 两处业务逻辑同步锁是两把锁,如果您将 lock 换成 this,这个现象就不会出现,读者可 以自己进行测试;
2 static 锁
如果我们的方法 ticket 是一个静态方法,再次测试一下您会发现,还是会出现 501 这 样的输出信息,根据之前的描述读者可能会第一时间想到他们两个用到的锁不是同一把锁, 因此我们将代码在次做了修改
public class TicketWindow3 implements Runnable { private static int max_value = 0; private boolean flag = true; @Override public void run() { if (flag) { while (true) { //同步函数其实用到的锁就是 this 锁, this锁针对的是对象 //静态锁,锁是类的字节码信息,因此如果一个类的函数为静态方法,那么我们需要通过该类的 class 信息进行加锁; synchronized (TicketWindow3.class) { if (max_value > 500) { break; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ":lock..." + max_value++); } } } else { while (true) if (ticket()) break; } } private synchronized static boolean ticket() { if (max_value > 500) { return true; } try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + ": method.." + max_value++); return false; } public void change() throws InterruptedException { Thread.sleep(30);// 读者可以自行思考为什么要sleep this.flag = false; } }
静态锁,锁是类的字节码信息,因此如果一个类的函数为静态方法,那么我们需要通过 该类的 class 信息进行加锁;