在类构造函数中调用线程的替代方法

时间:2018-07-14 14:09:31

标签: java multithreading thread-safety

我有一个可以被多个线程访问的类。我希望该类做一些事情 才能响应呼叫(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正在运行任务。我期待第一个线程执行长时间运行的任务的初始化和调用。

1 个答案:

答案 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