我正在学习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();
}
}
}
答案 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();
}
}
}
如果您还希望使它们依次开始,则应“同步”进行wait
和notify
调用的循环。
无论如何,关于多线程的要点是让多个线程在“相同”时间而不是按顺序运行,因为这与线性执行相同。
答案 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个任务。
我已经尽可能地完善了,但是有些观点可能有点棘手,所以请不要犹豫,要求澄清!