如何使我的线程按顺序运行?

时间:2019-04-16 08:53:00

标签: java

我正在学习Java线程,并希望我的代码按顺序输出线程0-9。我使用了synced关键字,但没有得到预期的结果。

我该怎么做才能更正我的代码?

public class MyThread extends Thread {

    private static final int threadMax = 10;
    private static int runCount = 0;

    public void printThread() {
        synchronized (this) {
            while (runCount++ < 100) {
                System.out.println(runCount + ": " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

    }

    public void run() {

        printThread();

    }

    public static void main(String[] args) {

        for (int i = 0; i < threadMax; i++) {
            new MyThread().start();
        }

    }

}

3 个答案:

答案 0 :(得分:1)

该功能不起作用,因为每次创建新的MyThread对象并在该新对象上进行同步时。因此,您创建的每个Thread都将锁定不同对象。因此,您应该传递一个通用对象来获得如下所示的锁。

class MyThread extends Thread {

    private static int runCount = 0;

    Object lock;

    public MyThread(Object lock) {
        this.lock = lock;
    }

    public void printThread() {
        synchronized (lock) {
           // your code here
        }

    }

 //.........
}

然后像这样调用它:

Object lock = new Object();
for (int i = 0; i < threadMax; i++) {
    new MyThread(lock).start();
}

但是,上述程序不能确保您可以按顺序运行。有几种方法可以做到这一点。您可以使用wait() and notify()实现目标。请参考以下示例:

public void printThread() {

        while (runCount < 90) {
            synchronized (lock) {
                while (runCount % 10 != remainder) {

                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

                System.out.println(runCount + ": " + Thread.currentThread().getName());
                runCount++;
                lock.notifyAll();
            }
        }

    }

并像这样调用线程:

    Object lock = new Object();
    for (int i = 0; i < 10; i++) {
        new MyThread(lock, i).start();
    }

答案 1 :(得分:0)

您正在同步线程的上下文,每个线程的上下文都不相同。您应该将synchronized键中所有不同线程的任何公共对象放入。这不会使它们以某种特定的安全性运行,而只是彼此等待结束。 如果要出于任何目的测试synchronized关键字,都可以向构造函数传递一个通用变量并在每个线程中使用它:

public class MyThread extends Thread {

private static final int threadMax = 10;
private static int runCount = 0;
private Object test; //Object pointing main method

public MyThread(Object test){
 this.test = test; //This won't copy values as it is an object and not a number, string...
}

public void printThread() {
    synchronized (test) { //Same object for all threads
        while (runCount++ < 100) {
            System.out.println(runCount + ": " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

public void run() {

    printThread();

}

public static void main(String[] args) {

    Object test; //common object
    for (int i = 0; i < threadMax; i++) {
        new MyThread(test).start();
    }

}

}

如果您还希望使它们依次开始,则应“同步”进行waitnotify调用的循环。

无论如何,关于多线程的要点是让多个线程在“相同”时间而不是按顺序运行,因为这与线性执行相同。

答案 2 :(得分:0)

您有几个任务要委派给线程,但要依次执行。

正如其他人所指出的,wait&notify可以帮助您实现:等到Nth完成然后通知下一个。但是,如果您在printThread方法中等待/通知,因为所有线程同时在同一锁上等待 ,则无法保证下一个第N + 1个线程。所以你可能有

1: thread-1
...
10: thread-1
11: thread-5
...
20: thread-5
21: thread-2
...

如果您还可以,那么您就完成了。但是,在您特别希望对线程进行排序的情况下,您需要的是等待队列(FIFO:先进先出)。

要实现这一目标,您可以使用很棒的ExecutorService。但是请注意,它们会向您隐藏Thread,因此选择该解决方案不应以事先了解它们的基本知识为代价。

ExecutorService是一个非常方便的类,可以接收任务(以Runnable的形式,请参见下文)并在单独的线程中执行它们。

在这里,我使用的是SingleThreadExecutor,它按顺序执行提交的任务。因此,您要做的就是调用它的execute方法,并将您的任务作为参数,ExecutorService将以正确的顺序运行它们,一个接一个。

以下是您可以做的一些笔记:

public class ThreadRunner {

    // Note : Constants are usually all uppercase in Java
    private static final int MAX_THREADS = 10;

    private final int threadName;

    public ThreadRunner(int threadName) {
        this.threadName = threadName;
    }

    public void printThread() {
        // Note: For loops are better than while when you already know the number of iterations
        for (int runCount = 0; runCount < 10; runCount++) {
            System.out.println(runCount + "th run from thread " + threadName);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        for (int i = 0; i < MAX_THREADS; i++) {
            int threadName = i + 1;
            // Submit a task to the executor
            executorService.execute(() -> new ThreadRunner(threadName).printThread());
        }

        // Nicely ask for the executor to shutdown.
        // Then wait for already submitted tasks to terminate.
        executorService.shutdown();
        try {
            executorService.awaitTermination(120, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

我更改了一些细节,原因如下:

创建线程:不要继承Thread

我建议您不要从Thread继承,而是创建它的本地实例,因为您所需要的只是使用一个Thread;您不想成为一个Thread

    public static void main(String[] args) {
        // Using Java 1.8+ lambda
        Thread lambdaThread = new Thread(() -> System.out.println("Hello from a lambda in a Thread"));
        lambdaThread.start();

        // Using an anonymous class for java <1.8
        Thread anonClassThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello from an anonymous class in a Thread");
            }
        });
        anonClassThread.start();
    }

您要使用lambda或匿名类(取决于您的Java版本)来创建一个传递Thread作为构造函数参数的新Runnable

Runnable只是将要执行的代码的一部分(在这种情况下,将由线程执行)。

ExecutorService的适用情况相同,它的execute方法采用了我通过lambda创建的Runnable

在线程之间共享static计数器

您的行private static int runCount = 0;是一个静态字段,这意味着它被类MyThread的所有实例共享。在线程中增加它时,所有线程都将读取(和写入)相同的变量。

如果线程按顺序运行,则第一个线程将执行100次迭代,然后当第二个线程启动时,runCount已经是100,并且您不会进入while循环。如果这不是故意的,那么在测试代码时可能会造成混乱。

基于注释中的预期输出,我相信您希望线程每个执行10次迭代,而不共享100个迭代的 pool 并设法以某种方式使每个线程仅执行10次。

具有线程名称的线程属于每个ThreadRunner

这里的小细节:以前,您创建了10个线程。在这里,ExecutorService 仅创建一个,他将其重复用于您提交的每个任务。因此Thread.currentThread().getName()将始终为thread-1。 没有此字段,您将无法查看正在运行的任务。

如果每个任务在上一个任务之后开始执行,则不需要10个线程,而是一个线程按顺序执行10个任务。

我已经尽可能地完善了,但是有些观点可能有点棘手,所以请不要犹豫,要求澄清!

相关问题