You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java线程同步失效排查:同步方法为何无法实现线程等待?

问题分析与代码修复

首先咱们得先揪出最致命的一个错误:你在循环里反复提交的是同一个B t1实例!Thread类的实例只能被启动一次,虽然你用ExecutorService.submit提交它(因为Thread实现了Runnable),但第一次执行完后,后续的submit不会再触发它的run方法执行,这会导致你的线程逻辑根本没按预期跑10次。这是第一个要改的地方——每次循环都应该创建新的B实例:executorService.submit(new B(c)),而不是复用t1。

接下来咱们逐个看四个选项里的同步问题:

选项1的问题

public class A{ 
    public static void main(String args[]){ 
        C c = new C(); 
        ExecutorService executorService = Executors.newFixedThreadPool(10); 
        B t1 = new B(c); 
        for(int i = 1; i <=10; i++) { 
            executorService.submit(t1); 
        } 
        executorService.shutdown(); 
        try{ 
            executorService.awaitTermination(Long.MAX_VALUE,TimeUnit.NANOSECONDS); 
        }catch(InterruptedException e){ 
            System.out.println("Error while checking tread life: "+e); 
        } 
    } 
} 
public class B extends Thread{ 
    C c; 
    static final Object lockObject = new Object(); 
    public B(C c) { this.c = c; } 
    public void run(){ someProcess(); } 
    synchronized public void someProcess(){ 
        String something = c.C(); 
        if(something == null || "".equalsIgnoreCase(something)){ 
            c.A(); 
        }else{ 
            c.B(); 
        } 
    } 
} 
public class C { 
    synchronized public void A(){ //insert the records } 
    synchronized public void B(){ //update the records } 
    synchronized public String C(){ // searching a record to get it's id if it exist return something; } 
}

这里的同步锁是完全分散的:

  • B.someProcess()用的是当前B实例作为锁(同步非静态方法的锁是this)
  • 而C的三个同步方法用的是当前C实例作为锁
    这就导致线程执行someProcess时,拿到的是B实例的锁,但调用c.C()、c.A()、c.B()时,又去拿C实例的锁,两个锁完全独立!多个线程可以同时进入someProcess(只要它们的B实例不同),而且即使进入了,调用C的方法时也可能和其他线程的C方法调用交叉执行,完全起不到同步整个“查询-判断-插入/更新”流程的作用。

选项2的问题

public class A { 
    public static void main(String args[]) { 
        C c = new C(); 
        ExecutorService executorService = Executors.newFixedThreadPool(10); 
        B t1 = new B(c); 
        for(int i = 1; i <=10; i++) { 
            executorService.submit(t1); 
        } 
        if(executorService !=null) { 
            executorService.shutdown(); 
            try { 
                executorService.awaitTermination (Long.MAX_VALUE,TimeUnit.NANOSECONDS); 
            }catch(InterruptedException e) { 
                System.out.println("Error while checking tread life: "+e); 
            } 
        } 
    } 
} 
public class B extends Thread { 
    C c; 
    static final Object lockObject = new Object(); 
    public B(C c) { this.c = c; } 
    public void run() { someProcess(); } 
    public void someProcess() { 
        synchronized(this) { 
            String something = c.C(); 
            if(something == null || "".equalsIgnoreCase(something)) { 
                c.A(); 
            }else{ 
                c.B(); 
            } 
        } 
    } 
} 
public class C { 
    synchronized public void A() { //insert the records } 
    synchronized public void B() { //update the records } 
    synchronized public String C() { // searching a record to get it's id if it exist return something; } 
}

和选项1本质完全一致:synchronized(this)锁的是B实例,而C的方法锁的是C实例,两个锁不共享。而且如果你复用同一个B实例,所有线程会抢同一个B实例的锁,但调用C的方法时,其他线程如果用不同的B实例(如果改了复用问题),还是会同时调用C的方法,导致流程不同步。

选项3的问题

public class A { 
    public static void main(String args[]) { 
        C c = new C(); 
        ExecutorService executorService = Executors.newFixedThreadPool(10); 
        B t1 = new B(c); 
        for(int i = 1; i <=10; i++) { 
            executorService.submit(t1); 
        } 
        if(executorService !=null) { 
            executorService.shutdown(); 
            try { 
                executorService.awaitTermination (Long.MAX_VALUE,TimeUnit.NANOSECONDS); 
            }catch(InterruptedException e) { 
                System.out.println("Error while checking tread life: "+e); 
            } 
        } 
    } 
} 
public class B extends Thread { 
    C c; 
    static final Object lockObject = new Object(); 
    public B(C c) { this.c = c; } 
    public void run() { someProcess(); } 
    public void someProcess() { 
        synchronized(this) { 
            String something = c.C(); 
            if(something == null || "".equalsIgnoreCase(something)) { 
                c.A(); 
            }else{ 
                c.B(); 
            } 
        } 
    } 
} 
public class C { 
    public void A() { synchronized(this) { //insert the records } } 
    public void B() { synchronized(this) { //update the records } } 
    public String C() { synchronized(this) { // searching a record to get it's id if it exist return something; } } 
}

这个选项只是把C方法的同步逻辑从方法级移到了方法内部,但锁还是C实例本身,和选项2的问题完全一致:B的someProcess用B实例锁,C的方法用C实例锁,锁不统一,整个查询-修改的流程还是会被打断。

选项4的问题

public class A { 
    public static void main(String args[]) { 
        C c = new C(); 
        ExecutorService executorService = Executors.newFixedThreadPool(10); 
        B t1 = new B(c); 
        for(int i = 1; i <=10; i++) { 
            executorService.submit(t1); 
        } 
        if(executorService !=null) { 
            executorService.shutdown(); 
            try { 
                executorService.awaitTermination (Long.MAX_VALUE,TimeUnit.NANOSECONDS); 
            }catch(InterruptedException e) { 
                System.out.println("Error while checking tread life: "+e); 
            } 
        } 
    } 
} 
public class B extends Thread { 
    C c; 
    static final Object lockObject = new Object(); 
    public B(C c) { this.c = c; } 
    public void run() { someProcess(); } 
    public void someProcess() { 
        synchronized(lockObject) { 
            String something = c.C(lockObject); 
            if(something == null || "".equalsIgnoreCase(something)) { 
                c.A(lockObject); 
            }else{ 
                c.B(lockObject); 
            } 
        } 
    } 
} 
public class C { 
    public void A(Object lockObject) { synchronized(lockObject) { //insert the records } } 
    public void B(Object lockObject) { synchronized(lockObject) { //update the records } } 
    public String C(Object lockObject) { synchronized(lockObject) { // searching a record to get it's id if it exist return something; } } 
}

这个选项的思路是对的:用一个全局静态锁lockObject来同步整个流程,但存在冗余问题——你在someProcess里已经加了lockObject的锁,然后在C的方法里又加了一次同一个锁,这属于重复加锁(虽然不会死锁,但完全没必要)。而且同样存在前面说的复用B实例的问题,导致线程没按预期执行。


正确的实现方式

要解决这个问题,核心是让整个“查询C -> 判断 -> 执行A/B”的流程用同一个锁来同步,同时保证每个线程都是独立的B实例。修改后的代码可以这样:

方式一:用共享的C实例作为锁(推荐,因为所有线程共享同一个C实例)

public class A{ 
    public static void main(String args[]){ 
        C c = new C(); 
        ExecutorService executorService = Executors.newFixedThreadPool(10); 
        // 每次循环创建新的B实例,保证线程独立执行
        for(int i = 1; i <=10; i++) { 
            executorService.submit(new B(c)); 
        } 
        executorService.shutdown(); 
        try{ 
            executorService.awaitTermination(Long.MAX_VALUE,TimeUnit.NANOSECONDS); 
        }catch(InterruptedException e){ 
            System.out.println("Error while checking thread life: "+e); 
        } 
    } 
} 

// 没必要继承Thread,实现Runnable更灵活通用
public class B implements Runnable{ 
    private C c; 
    public B(C c) { this.c = c; } 
    public void run(){ 
        someProcess(); 
    } 
    public void someProcess(){ 
        // 用共享的C实例作为锁,保证整个流程同步
        synchronized(c) {
            String something = c.C(); 
            if(something == null || "".equalsIgnoreCase(something)){ 
                c.A(); 
            }else{ 
                c.B(); 
            } 
        }
    } 
} 

public class C { 
    // 外面已经用c实例加锁了,这里不需要再同步
    public void A(){ 
        //insert the records 
    } 
    public void B(){ 
        //update the records 
    } 
    public String C(){ 
        // searching a record to get it's id if it exist return something; 
        return "";
    } 
}

方式二:用静态全局锁

public class B implements Runnable{ 
    private C c; 
    // 静态全局锁,所有B实例共享
    private static final Object lock = new Object();
    public B(C c) { this.c = c; } 
    public void run(){ 
        someProcess(); 
    } 
    public void someProcess(){ 
        synchronized(lock) {
            String something = c.C(); 
            if(something == null || "".equalsIgnoreCase(something)){ 
                c.A(); 
            }else{ 
                c.B(); 
            } 
        }
    } 
} 
// A和C类和上面一致,C的方法不需要额外加锁

这样修改后,每个线程都会独立执行,而且整个查询-判断-修改的流程会被同一个锁保护,线程之间会互相等待,不会出现交叉执行的情况。

内容的提问来源于stack exchange,提问作者yatinbc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:24:42