Java同步机制无法实现互斥的问题咨询
咱们先拆解下核心问题:你提到多个Node实例调用Receive类的synchronized方法rcv()时,输出还是混杂,说明互斥锁根本没生效。最常见的原因是——你的多个Node可能各自持有独立的Receive实例,而synchronized实例方法的锁是当前对象实例,不是类本身。也就是说,每个Receive实例的锁是分开的,线程们各拿各的锁,自然不会互斥。
先确认问题根源
先对照下你的代码是不是类似这种情况:
public class Receive { public synchronized void rcv() { // 输出逻辑,比如打印当前Node的信息 System.out.println("Node " + Thread.currentThread().getName() + " is receiving..."); // 模拟耗时操作,放大混杂情况 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Node " + Thread.currentThread().getName() + " finished receiving"); } } public class Main { public static void main(String[] args) { for (int i = 0; i < 3; i++) { // 每个Node都new一个独立的Receive实例 new Node(new Receive()).start(); } } } class Node extends Thread { private Receive receive; public Node(Receive receive) { this.receive = receive; } @Override public void run() { receive.rcv(); } }
如果是这样,问题就很明确了:每个Node对应的Receive实例都不一样,synchronized void rcv()的锁是各自的Receive对象,线程之间根本不会竞争同一把锁,所以输出会混杂。
两种靠谱的解决方案
根据你的业务需求,选下面其中一种就行:
方法1:让所有Node共享同一个Receive实例
既然synchronized实例方法的锁是对象本身,那让所有Node共用一个Receive对象,所有线程就会竞争同一把锁:
public class Main { public static void main(String[] args) { // 只创建一个Receive实例,传给所有Node Receive sharedReceive = new Receive(); for (int i = 0; i < 3; i++) { new Node(sharedReceive, "Node-" + i).start(); } } } class Node extends Thread { private Receive receive; public Node(Receive receive, String name) { super(name); this.receive = receive; } @Override public void run() { receive.rcv(); } }
方法2:使用类锁替代对象锁
如果你不想共享Receive实例,那可以把rcv()改成静态同步方法,或者在方法内部用类锁——类锁是绑定在Receive.class对象上的,不管有多少Receive实例,所有线程都会竞争这同一把锁:
// 方案2.1:静态同步方法 public class Receive { public static synchronized void rcv() { System.out.println("Node " + Thread.currentThread().getName() + " is receiving..."); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Node " + Thread.currentThread().getName() + " finished receiving"); } } // 方案2.2:实例方法内部用类锁 public class Receive { public void rcv() { synchronized (Receive.class) { System.out.println("Node " + Thread.currentThread().getName() + " is receiving..."); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Node " + Thread.currentThread().getName() + " finished receiving"); } } }
验证效果
不管用哪种方法,运行后你会看到每个Node的输出都是完整的:先打印某个Node的"is receiving...",等它执行完耗时操作,再打印"finished receiving",才会轮到下一个Node,不会再出现输出混杂的情况。
最后提个小提醒:如果Receive类里有其他非线程安全的成员变量,也要确保这些变量的访问都在锁的保护下,不然还是可能出现线程安全问题哦~
内容的提问来源于stack exchange,提问作者parsa

