同步方法内新线程调用同步方法为何阻塞?如何异步执行?
问题原因
Java中实例级同步方法的锁是当前对象的实例(this),同一个对象的所有实例同步方法共用这把锁。
当主线程调用synchronized修饰的fooMethod时,会立即持有当前对象的锁,直到整个方法执行完毕才会释放。新线程启动后循环调用的dummyMethod同样是实例同步方法,需要获取同一把锁,但此时fooMethod还在执行(Thread.sleep(10000)不会释放同步锁),所以新线程会被阻塞,直到fooMethod执行完成、锁被释放后,才能继续执行剩余的dummyMethod调用。
你看到的“立即执行1次”是因为主线程启动新线程后,在进入sleep前的极短时间内,新线程刚好抢到了一次锁(或是日志输出的时序问题),但后续调用因为锁被主线程持有,只能等待。
解决方案
要让新线程能异步调用dummyMethod,核心是避免两个方法争抢同一把锁,以下是两种可行方案:
方案1:使用独立的锁对象
给fooMethod和dummyMethod分别指定不同的锁对象,替换默认的this锁:
// 定义两个独立的锁对象 private final Object fooLock = new Object(); private final Object dummyLock = new Object(); private void fooMethod() throws InterruptedException { // 使用fooLock作为当前方法的锁 synchronized (fooLock) { new Thread(() -> { for (int i = 0; i < 10; i++) { dummyMethod(); } }).start(); Thread.sleep(10000); Log.d("debug", "fooMethod() finished"); } } private void dummyMethod() { // 使用dummyLock作为当前方法的锁 synchronized (dummyLock) { Log.d("debug", "do nothing in dummyMethod()"); } }
方案2:调整同步范围(如果业务允许)
如果fooMethod的同步不需要覆盖整个方法,仅需要同步部分逻辑,可以缩小同步范围,让锁更早释放:
private void fooMethod() throws InterruptedException { // 仅同步需要保护的核心代码块,而非整个方法 synchronized (this) { // 此处放置需要同步的业务逻辑 } // 启动新线程时,锁已经释放 new Thread(() -> { for (int i = 0; i < 10; i++) { dummyMethod(); } }).start(); Thread.sleep(10000); Log.d("debug", "fooMethod() finished"); } private synchronized void dummyMethod() { Log.d("debug", "do nothing in dummyMethod()"); }
注意:Thread.sleep()不会释放同步锁,只有当同步块/方法执行完毕、或者调用wait()方法时,才会释放持有的锁。
内容的提问来源于stack exchange,提问作者Hong
相关产品推荐
相关产品推荐

