另一个线程进入睡眠状态后移至主线程

时间:2019-07-31 13:43:58

标签: java multithreading

我有一个主线程和一个并行运行的线程,我希望能够先运行该线程,然后再移至其他任何线程,例如我的主线程。

我试图查看Google,但是找不到答案。

public class AutoUpdater implements Runnable {
    public void run() {
        System.out.println("Thread is running...");
        for (int i = 0; i < clients.size(); i++) {
            do something...
        }
        System.out.println("Thread ended.\n");
        int time = 1000 * 60 * 60 * 24;
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            System.out.println("Something interrputed thread while running.");
        }
    }

public class Main {
            public static void main(String[] args) throws IOException, ClassNotFoundException {

                Runnable runnable = new AutoUpdater(clients);
                Thread thread = new Thread(runnable);
                thread.start();
                        // run after the above thread finish and go to sleep
                System.out.println("This is a test");
            }

就像我在上面说的那样,我希望我的线程完成并进入X睡眠时间,例如24小时,然后在进入睡眠状态时移回我的主线程。

目标是建立一个首先更新所有客户帐户的银行系统,然后运行的方法(我的第二个线程)将在接下来的24小时内进入睡眠状态。然后移回我的主体。

1 个答案:

答案 0 :(得分:1)

您在上面的代码中所做的操作创建了一个与主线程同时运行的线程。实际发生的是:

  1. 主线程启动并启动AutoUpdater线程
  2. 两个线程将同时运行。实际上,Main线程甚至可能在AutoUpdater线程真正启动之前终止。
  3. 自动更新线程一次处理客户端,然后休眠24小时,然后终止,此时您的程序完全终止。

因此,坚持使用现有的内容,第一步是使AutoUpdater线程每24小时运行一次。可以执行此操作的一种方法是保持线程运行,并在run方法中放置一个while循环,这样它不会终止,而是每24小时处理一次clients集合。所以现在AutoUpdater可能看起来像这样:

public class AutoUpdater implements Runnable {
    public void run() {
        while (true) {
            try {
                System.out.println("Thread is running...");
                for (int i = 0; i < clients.size(); i++) {
                    // do something...
                }
            } finally {
                System.out.println("Thread ended.\n");
            }

            int time = 1000 * 60 * 60 * 24;
            try {
                Thread.sleep(time);
            } catch (InterruptedException e) {
                System.out.println("Something interrputed thread while running.");
            }
        }
    }
}

但是,上面的代码存在一些问题,因为它会漂移。例如,如果处理需要一个小时,那么下一次运行将是在上次运行初始开始后25小时。幸运的是,Java提供了一个线程执行程序服务,该服务将按照固定的时间表ScheduledExecutorService运行线程。因此,让我们放松while循环并引入执行程序。

public class AutoUpdater implements Runnable {
    public void run() {
        System.out.println("Thread is running...");
        for (int i = 0; i < clients.size(); i++) {
            // do something...
        }
        System.out.println("Thread ended.\n");
    }
}

public static class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(
                    new AutoUpdater(clients, lock.writeLock()),
                    0,
                    24,
                    TimeUnit.HOURS);

        System.out.println("This is a test");
    }
}

现在,自启动进程以来,我们每24小时运行一次自动更新程序线程。如果您想确定时间,例如每天8点,您可以计算到该时间的延迟(尽管这不会考虑夏时制问题),也可以使用Quartz这样的第三方库来安排特定时间一天中的时间。

  

我希望能够先运行该线程,然后再移至其他任何线程,例如我的主线程。

大概是这样,您的意思是要在自动更新运行时停止其他线程的执行。为此,您有几个可用的选项。在第一种情况下,您可以使用监视器来同步和锁定线程,即

Object sharedMonitor = new byte[0]

// In your auto-updater and other threads
synchronised(sharedMonitor ) {

}

以上语法仅允许单个线程一次为同一监视器实例输入synchronized块。在上面只有两个线程的示例中,这可以很好地工作。如果您拥有两个以上的线程,那么它将成为问题,因为您只希望在自动更新程序运行时阻塞其他线程。在这种情况下,这不是适合您的解决方案。您所要做的就是让所有线程同时运行,直到需要运行自动更新程序,然后它们都需要阻塞并等待自动更新程序完成。对您来说幸运的是,Java有一个ReadWriteLock可以做到这一点。

因此,我们添加该锁并使用它。

public static class Main {
    private static List<String> clients = new ArrayList<>();

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ReadWriteLock lock = new ReentrantReadWriteLock();

        ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
        executor.scheduleAtFixedRate(
                new AutoUpdater(clients, lock.writeLock()),
                0,
                24,
                TimeUnit.HOURS);

        Lock readLock = lock.readLock();
        while (true) {
            try {
                readLock.lock();
                System.out.println("This is a test");
            } finally {
                readLock.unlock();
            }
        }
    }
}

所以上面有:

  1. 添加了读写锁
  2. 将写锁传递给AutoUpdater
  3. 在main方法中添加了while循环,这样它不会终止,并且可以执行它打算执行的任何常规处理。
  4. while循环中,我们从一开始就获得了读取锁定,并在结束时释放了读取锁定。

难题的最后一步是使用AutoUpdater

中的写锁
public class AutoUpdater implements Runnable {
    public void run() {
        try {
            lock.lock();
            System.out.println("Thread is running...");
                // do something...
            }
        } finally {
            System.out.println("Thread ended.\n");
            lock.unlock();
        }
    }
}