我有一个可以被多个线程访问的类。我希望该类做一些事情 才能响应呼叫(getSomething)。
我当时正在考虑在类构造函数中启动我的SampleThread,但是我不喜欢在内部启动线程的想法 构造函数。
我正在考虑做这样的事情,但是我不确定这是否正确。 将在我的课程中调用getSomething的第一个线程将启动一个线程。
但是我仍然不确定这是否正确。。我担心会运行多个SampleThread,而我希望它只运行一次。
public class A{
private final AtomicBoolean isReady = new AtomicBoolean(false);
public A{
}
public void getSomething(){
if(!isReady.get()){
new SampleThread().start();
}
//continue with the rest of the method
}
}
public class SampleThread{
public void run(){
//Do some long running task once done
isReady.set(true);
}
}
我没有办法添加一个名为start()的方法,因为框架正在调用该方法,所以我可以在其中调用我的SampleThread。
有任何提示吗?
更新2
我尝试了一个示例类来对此进行模拟,然后使用闩锁等待InitializerThread完成。
package com.race;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
public class TestRaceCondition {
public static class SampleClass implements Runnable {
private final CountDownLatch latch = new CountDownLatch(1);
private final AtomicBoolean isReady = new AtomicBoolean(false);
public void doSomething() {
synchronized (this) {
if (!isReady.get()) {
System.out.println(Thread.currentThread().getName() + " is initializing the system....");
new InitializerThread(latch).start();
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
isReady.set(true);
}
}
System.out.println("Doing something...." + Thread.currentThread().getName());
System.out.println("Still doing something...." + Thread.currentThread().getName());
}
@Override
public void run() {
// System.out.println(Thread.currentThread().getName() + " :: is running!");
doSomething();
}
}
public static class InitializerThread extends Thread {
private CountDownLatch latch;
public InitializerThread(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
// Simulate some long running task
System.out.println(Thread.currentThread().getName() + " is calling a long running task....");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
latch.countDown();
}
}
public static void main(String[] args) {
SampleClass myClass = new SampleClass();
Thread t1 = new Thread(myClass);
Thread t2 = new Thread(myClass);
Thread t3 = new Thread(myClass);
Thread t4 = new Thread(myClass);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
但是我不确定结果有什么问题。
Initializing system....
Calling a long running task....
Initializing system....
Doing something....Thread-0
Still doing something....Thread-0
Doing something....Thread-3
Still doing something....Thread-3
Calling a long running task....
Initializing system....
Doing something....Thread-2
Still doing something....Thread-2
Calling a long running task....
Initializing system....
Doing something....Thread-1
Still doing something....Thread-1
Calling a long running task....
似乎它仍然多次调用我的初始化器线程... 我添加了闩锁,但是输出出现了问题。
我期待这样的事情...
Initializing system....
Calling a long running task....
Doing something....Thread-0
Still doing something....Thread-0
Doing something....Thread-3
Still doing something....Thread-3
Doing something....Thread-2
Still doing something....Thread-2
Doing something....Thread-1
Still doing something....Thread-1
更新3 我根据建议编辑了代码,以包括同步...
Thread-0 is initializing the system....
Thread-4 is calling a long running task....
Doing something....Thread-0
Still doing something....Thread-0
Doing something....Thread-2
Still doing something....Thread-2
Doing something....Thread-3
Still doing something....Thread-3
Doing something....Thread-1
Still doing something....Thread-1
我只是没有得到输出。.为什么Thread-0正在执行初始化,但是Thread-4正在运行任务。我期待第一个线程执行长时间运行的任务的初始化和调用。
答案 0 :(得分:2)
这种方式具有比赛条件:
public void getSomething(){
if(!isReady.get()){
new SampleThread().start();
}
//continue with the rest of the method
}
这是原子的:if(!isReady.get())
,但是与之关联的条件语句的主体不是:
{
new SampleThread().start();
}
因此您可以启动两次该线程。
同步逻辑可防止出现竞争情况。这也会增加对象上潜在的锁定数量,但是由于if(!isReady.get())
应该被快速执行,因此应该可以接受。
请注意,如果布尔值仅在同步语句中使用,则可能不需要使用AtomicBoolean
。
因此,这里有两种根据您的要求的方法。
1)为了允许getSomething()
的首次调用开始SampleThread
,并且其他线程在执行getSomething()
之前等待初始化结束:
public void getSomething(){
synchronized(this){
// init the logic
if(!isReady){
SampleThread t = new SampleThread();
t.start();
t.join(); // wait for the SampleThread thread termination
isReady.set(true);
}
// execute the next only as the init thread was terminated
if(isReady){
//continue with the rest of the method
}
}
}
2)为了允许getSomething()
的首次调用开始SampleThread
,并且其他线程在执行getSomething()
之前不等待初始化结束:
public void getSomething(){
synchronized(this){
// init the logic once
if(!isReady.get()){
SampleThread t = new SampleThread();
t.start();
}
}
//continue with the rest of the method
}
然后在isReady
的{{1}}的末尾将true
设置为run()
:
SampleThread