synchronized锁住的是代码还是对象
不同的对象
public class Sync { public synchronized void test() { System.out.println("test start"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test end"); } }
#
public class MyThread extends Thread{ public void run() { Sync sync = new Sync(); sync.test(); } public static void main(String[] args) { for (int i = 0; i < 3; ++i) { Thread thread = new MyThread(); thread.start(); } } }
执行结果
test start test start test start test end test end test end
现象
在MyThread中,每次都new一个新的Sync对象,可以看到代码块test虽然被加了synchonized但是还是并行执行的,初步结论:锁住的不是代码块
同一个对象
public class MyThread2 extends Thread{ public Sync sync; MyThread2(Sync sync) { this.sync = sync; } public void run() { System.out.println("hi...."); sync.test(); } public static void main(String[] args) { Sync sync = new Sync(); for (int i = 0; i < 3; ++i) { Thread thread = new MyThread2(sync); thread.start(); } }
执行结果
hi.... test start hi.... hi.... test end test start test end test start test end
现象
可以看到当他们共用一个对象的时候,synchonized起了作用,这块代码是串行执行的
结论
锁住的是对象
如何在多对象的时候任然锁住代码?
解决也很简单,只要锁住同一个对象就行了。例如:synchronized后的括号中锁同一个固定对象,这样就行了。
这样是没问题,但是,比较多的做法是让synchronized锁这个类对应的Class对象。
public class Sync2 { public void test() { synchronized (Sync2.class) { System.out.println("test start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test end"); } } }
#
public class MyThread3 extends Thread{ public void run() { Sync2 sync = new Sync2(); sync.test(); } public static void main(String[] args) { for (int i = 0; i < 3; ++i) { Thread thread = new MyThread3(); thread.start(); } } }
执行结果
test start
test end
test start
test end
test start
test end
synchronized的两大不足
- 由于我们没办法设置synchronized关键字在获取锁的时候等待时间,所以synchronized可能会导致线程为了加锁而无限期地处于阻塞状态
- 使用synchronized关键字等同于使用了互斥锁,即其他线程都无法获得锁对象的访问权。这种策略对于读多写少的应用而言是很不利的,因为即使多个读者看似可以并发运行,但他们实际上还是串行的,并将最终导致并发性能的下降。
虽然synchronized已经作为一个关键字被固化在Java语言中了,但它只提供了一种相当保守的线程安全策略,且该策略开放给程序员的控制能力极弱
参考