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

标签 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 正在运行该任务。我期望第一个线程执行长时间运行任务的初始化和调用。

最佳答案

这种方式存在竞争条件:

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()) 应该快速执行,因此应该可以接受。
请注意,如果 boolean 值仅在同步语句中使用,您可能不需要使用 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       
}

并在 SampleThreadrun() 末尾将 isReady 设置为 true :

public void run(){
    //Do some long running task once done
    isReady.set(true);
}

关于java - 在类构造函数中调用线程的替代方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51340270/

相关文章:

java - SimpleDateFormat 的线程安全问题

java - 方法级单例实例线程安全吗?

java - Selenium +Java : Parallel execution for test cases having login functionality

java - JSF 2 : Create a custom component with CommandButton and get attribute to ActionListener

java - 为什么 JMX 报告的 JVM 堆使用最大值会随时间变化?

java - 如何正确停止ServerSocket线程?关闭套接字失败

java - Android,下载 .txt 文件并在内部保存

c - PThread 互斥体未按预期工作

java - 线程何时释放对象上的锁?

data-structures - 为什么 Vec 有 trait Sync?