所以,我是线程新手,而且我还在学习一切是如何工作的。所以,我找不到能够解释我的问题的答案(根据我的理解程度)。
我有一个Runnable
类看起来像这样:
public class Request implements Runnable {
private Boolean ok = true;
public synchronized void setOk(Boolean ok) {
this.ok = ok;
}
public synchronized Boolean getOk() {
return ok;
}
private synchronized void foo() {
//if something happens
setOk(false);
}
@Override
public void run() {
while (true)
foo();
}
}
然后我有另一个类执行以下操作:
private static Request request;
private static void spawnThreads() {
ExecutorService e = new Executors.newFixedThreadPool(4);
request = new Request();
e.execute(request);
}
public static void main(String[] args) {
spawnThreads();
while (true) {
System.out.println(request.getOk());
if (!request.getOk())
request.setOk(true);
TimeUnit.SECONDS.sleep(10);
}
}
我需要,如果在主线程中,getOk()
返回false
,请执行某些操作并将其设置为true
。 Viceversa,在线程中将其设置为false
(无论ok
在任何给定时间的值是什么,我都需要继续前进。)
正如此代码所示,我无法在主线程中获得request.getOk()
的值。如果我从getter和setter中删除synchronized
个单词,我可以访问主线程中的值,直到线程更改它的时间点,并且永远不会再次。
此外,使用执行程序是因为我会创建多个Request
对象,并且在访问变量之前等待它关闭会与我执行此操作的原因相矛盾,因为我需要所有线程继续运行。
该线程向服务器发出http请求(随机超时,拒绝响应等)并用于检索某些信息。当线程从服务器获取ok响应和一些信息时,ok
变量会记录下来。
如何解决它,以便线程可以更新该变量,但主线程能够在需要时检索它,无论它是否同时被线程更改。
将我的Runnable
更改为Callable
有用吗?如果是,怎么样?
答案 0 :(得分:0)
你的例子仍然在线程安全中留下了一些漏洞。如果正确使用,@ Radiodef使用AtomicBoolean
提到的内容可以减轻大部分同步。
使用您的示例,这是一个线程安全的Request类,它接受一条消息,就像对http请求的回答一样。
public final class Request implements Runnable {
private final AtomicBoolean ok = new AtomicBoolean(false);
// volatile variables promote reference changes through all threads
private volatile String msg;
private boolean setMessage(String responseMessage) {
if (this.ok.compareAndSet(false, true)) {
this.msg = msg;
return true;
}
return false;
}
public boolean hasMessage() {
// *pure* getters don't need synchronisation!
return this.ok.get();
}
public String getMessageAndReset() {
// make a copy before resetting the OK
String msgCopy = this.msg;
this.ok.compareAndSet(true, false);
return msgCopy;
}
public void run() {
final Random rand = new Random();
try {
while(true) {
// sleep at random max 5 seconds
// (simulate unpredictable network)
TimeUnit.SECONDS.sleep(rand.nextInt(5));
while(!setMessage("Incoming message")) {
// busy waiting ... waits until the current value has
// been retrieved by the main thread
Thread.sleep(100);
}
}
} catch (Exception e) {
System.out.println(e);
}
}
}
你的主要班级:
public final class MainClazz implements Runnable {
private final ExecutorService exec;
private final Request request;
public void MainClazz() {
this.exec = new Executors.newFixedThreadPool(4);
this.request = new Request();
this.exec.execute(request);
}
public void run() {
while (true) {
if (request.hasMessage()) {
System.out.println(request.getMessageAndReset());
}
TimeUnit.SECONDS.sleep(10);
}
public static void main(String[] args) {
MainClazz main = new MainClazz();
main.run();
}
}
在此实现中,Request
类一次只保存一个值。根据您希望考虑使用缓冲区的数据量。
此外,和许多其他人一样,不要使用while (true)
!从java concurrent package获取同步对象!
AtomicBoolean对象的更多轻读。