Java多线程-synchronized(非this对象)
3个结论
synchronized(非this对象 x) 是将 x 对象本身作为“对象监视器”,因此有如下三个结论:
1)当多个线程同时执行 synchronized(x){}同步代码块时呈同步效果
2)当其他线程执行 x 对象中 synchronized 同步方法时呈同步效果
3)当其他线程执行 x 对象方法里的 synchronized(this) 代码块时也呈现同步效果。
静态同步方法 与 synchronized(class)代码块
关键字 synchronized 还可以应用在 static 静态方法上,如果这样写,则是对当前“类对象”进行持锁
注意区别:
synchronized 作用在非 static 方法上时,是“对象锁”,作用到 static 方法上时,是“Class锁”
示例:
public class SyncNotThis { public static void main(String[] args) { Service service = new Service(); Thread t1 = new Thread(() -> { Service.printA(); }); Thread t2 = new Thread(() -> { Service.printB(); }); Thread t3 = new Thread(() -> { service.printC(); }); t1.start(); t2.start(); t3.start(); } static class Service { /** * 静态方法 A */ synchronized public static void printA() { try { System.out.println("线程名称为:" + Thread.currentThread().getName() + "开始执行 A 方法"); Thread.sleep(1000); System.out.println("线程名称为:" + Thread.currentThread().getName() + "结束执行 A 方法"); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 静态方法B */ synchronized public static void printB() { try { System.out.println("线程名称为:" + Thread.currentThread().getName() + "开始执行 B 方法"); Thread.sleep(1000); System.out.println("线程名称为:" + Thread.currentThread().getName() + "结束执行 B 方法"); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 非静态方法C */ synchronized public void printC() { try { System.out.println("线程名称为:" + Thread.currentThread().getName() + "开始执行 C 方法"); Thread.sleep(500); System.out.println("线程名称为:" + Thread.currentThread().getName() + "结束执行 C 方法"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果如图:
可以发现 方法 A、B的运行呈现同步效果,方法C与 A、B呈现异步效果,异步的原因是方法 C与 方法 A、B持有的是不同的锁
synchronized(String对象)的特殊情况
因为JVM中String常量池的存在,会导致 synchronized锁到同一对象
例:
public class SyncString { public static void main(String[] args) { Thread t1 = new Thread(() -> { String aa = "AA"; print(aa); }); Thread t2 = new Thread(() -> { String aa = "AA"; print(aa); }); t1.start(); t2.start(); } public static void print(String stringParam) { synchronized (stringParam) { try { System.out.println(Thread.currentThread().getName() + "begin"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + "end"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果如下:
因为String 常量池导致了锁的是同一 String 对象,所以两个线程是同步执行的
锁对象的改变
示例:
public class LockChange { public static void main(String[] args) { MyService service = new MyService(); MyThread t1 = new MyThread(service, "A"); MyThread t2 = new MyThread(service, "B"); t1.start(); t2.start(); } static class MyThread extends Thread { private MyService service; public MyThread(MyService service, String name) { super(name); this.service = service; } @Override public void run() { service.testMethod(); } } static class MyService { private String lock = "123"; public void testMethod() { try { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis()); lock = "456"; Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis()); } } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果如下:
分析:
线程A 和 线程 B持有的锁都是 “123”,虽然 lock 的引用从 "123" 变成了 "456",但是 A 和 B 争抢的锁还是 “123” ,所以尽管线程 A中间改变了 lock 的引用,但是结果还是同步的(或者换一种理解:synchronized 锁的是引用指向的对象,而非引用本身)