我一直在阅读很多关于线程的内容,但我不确定处理这种情况的正确方法。假设我有一个引用对象的实用程序类。实用程序类可以处理对象,可以从多个线程调用它
class Utility{
Foo xx;
public Utility(Foo xx){
this.xx = xx;
}
public void doWork(){
x.action();
}
}
现在,如果我有两个线程将访问实用程序类并调用doWork(),其中xx将始终引用同一个对象,我该如何使其线程安全?
编辑:实用程序类有2个实例(每个线程一个),但引用的xx对象相同。请参见编辑:
public class ThreadSafety {
public static void main(String[] args) throws InterruptedException {
Foo xx = new Foo();
Utility util1 = new Utility(xx);
Utility util2 = new Utility(xx);
Thread t1 = new Thread(new MyRunnable(util1) , "t1");
Thread t2 = new Thread(new MyRunnable(util2) , "t2");
t1.start();
t2.start();
t1.join();
t2.join();
}
}
class MyRunnable implements Runnable{
Utility util;
@Override
public void run() {
util.doWork();
}
private void MyRunnable (Utility util) {
this.util = util;
}
}
答案 0 :(得分:3)
答案 1 :(得分:3)
public class Utility{
private final Foo xx;
public Utility(Foo xx){
this.xx = xx;
}
public void doWork() {
// ensures only one thread can be calling action
synchronized(xx) {
xx.action();
}
}
}
以下几点变化:
xx
final
和private
。使其private
强制控制如何访问xx
。synchronize
之前,xx
上的doWork
。您可以将synchronized
放在方法声明中,但我更愿意明确您要锁定的对象。请注意,这不是“同步xx”。它只使用xx
作为锁。在访问可能存在危险的代码块的任何地方,必须使用具有相同监视器对象的synchronized块。答案 2 :(得分:2)
您可以使用synchronized
关键字。
public synchronized void doWork(){
x.action();
}
这将使方法线程安全。有关Oracle documentation上的同步方法的更多信息。
使用更新后的问题: 使用静态方法作为synchronized,然后锁将在不在单个对象上的类上。
答案 3 :(得分:2)
以下是另一种建议:不使用synchronized
。使用synchronized的代码往往很复杂,错误,无法测试。你会遇到问题,你永远不会知道什么时候会爆炸,即使它有效,你也可能会遇到糟糕的表现。这样做:
创建一个不可变类,您将调用Message
来在线程之间进行通信,并使用BlockingQueue
Message
其他线程存储的消息由你的主线程处理。然后,在主线程中,只需从队列中弹出消息并处理它们。简单(* 1),简单,可测试,万无一失。
BlockingQueue
是使用synchronized
在内部实现的,它是由知道自己在做什么的人编写的,并且已经过全面测试,因此可行。这是唯一应该包含synchronized
的地方。
(* 1)起初看起来似乎并不像在一些地方粘贴synchronized
关键字那么容易,但事实上,从长远来看,synchronized
总是会出现比起最初似乎要复杂得多,难以做到。
答案 4 :(得分:2)
如果您要更改数据,请考虑使用不可变对象,而不仅仅是简单地使用synchronized关键字。
读