我最近开始使用Java多线程
我在解决一个问题时遇到问题,我只有5个线程,范围从 T1,T2,... T5 。
任务是按以下顺序打印从1到10的数字。
T1 -> 1
T2 -> 2
T3 -> 3
T4 -> 4
T5 -> 5
T1 -> 6
T2 -> 7
T3 -> 8
T4 -> 9
T5 -> 10
我试图用这段代码解决它。
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread[] tArray = new Thread[] { new Thread(counter, "T1"), new Thread(counter, "T2"),
new Thread(counter, "T3"), new Thread(counter, "T4"), new Thread(counter, "T5") };
for (int i = 0; i < 10; i++) {
if (i < 5) {
tArray[i].start();
tArray[i].join();
} else {
tArray[i - 5] = new Thread(counter, "T" + ((i - 5) + 1)); //Instantiating new Thread which is not allowed.
tArray[i - 5].start();
tArray[i - 5].join();
}
}
}
public class Counter implements Runnable {
private int count = 0;
@Override
public synchronized void run() {
System.out.println(Thread.currentThread().getName() + " -> " + ++count);
}
}
但是由于只允许5个线程,所以我的解决方案不被接受,因为我还在new Thread
循环的else
块中实例化了for
。
任何帮助将不胜感激。
答案 0 :(得分:5)
您需要安排线程之间的交互。线程交互的最自然的方法是建立连接线程的阻塞队列。队列可以是独立的对象,也可以属于特定线程。 在您的情况下,您需要绕5个线程。
class CountPrinter extends Thread {
String name;
ArrayBlockingQueue<Integer> inp = new ArrayBlockingQueue<>();
CountPrinter next;
public void run() {
for (;;)
int n = inp.take();
if (n == 11) {// only 10 numbers must be printed
next.inp.put(11);
return;
}
System.out.println(name+"->"+n);
next.inp.put(n+1);
}
}
}
在创建线程之后和开始之前,您需要分配字段name
和next
。我相信您可以自己对此进行编程。
另外,必须为第一个线程提供初始值1
。
答案 1 :(得分:1)
免责声明:我正在回答OP问题的实际对应部分-带有串行输入和输出的并行处理。有趣得多。
System.out
。无论我如何构造代码,都将在其前面出现显式或隐式队列/争用。Produce
→Stringize
→Output
。Stringize
步骤可以并行完成,只要有序的Output
仍然可以发生。我从一个快速且肮脏的“穷人”解决方案开始。对于Java 8
,这将与CompletableFuture
-s:
final Executor inputWorker = newSingleThreadExecutor();
final Executor processingPool = newFixedThreadPool(3);
final Executor outputWorker = newSingleThreadExecutor();
final int[] counter = {-1}; // this emulates a non-thread-safe information source
CompletableFuture<Void> future = completedFuture(null);
for (int i = 0; i < 10; ++i) {
future = future // chaining of futures is essential for serializing subsequent iterations
.thenApplyAsync(unused -> ++counter[0], inputWorker)
.thenApplyAsync(Objects::toString, processingPool)
.thenAcceptAsync(System.out::println, outputWorker);
}
future.join();
P.S。 -为完整起见,可能希望第5步略有不同,首先创建整个计算时间表,然后触发它:
final Executor producer = newSingleThreadExecutor();
final Executor stringizer = newFixedThreadPool(3);
final Executor printer = newSingleThreadExecutor();
final int[] counter = {-1}; // this emulates a non-thread-safe information source
System.out.println("creating schedule...");
// first schedule the whole amount of work and block the execution on a single "trigger" future
final CompletableFuture<Void> trigger = new CompletableFuture<>();
CompletableFuture<Void> future = trigger;
for (int i = 0; i < 10; ++i) {
future = future
.thenApplyAsync(unused -> ++counter[0], producer)
.thenApplyAsync(Objects::toString, stringizer)
.thenAcceptAsync(System.out::println, printer);
}
// then pull the trigger
System.out.println("pulling the trigger...");
trigger.complete(null);
future.join();
答案 2 :(得分:0)
另一种方法是保留两个OnStart
,如下所示:
AtomicInteger
用法为:
static class MyRunnable implements Runnable {
private final AtomicInteger index;
private final AtomicInteger ai;
private final int[] array;
private final int current;
private final int next;
public MyRunnable(AtomicInteger index, AtomicInteger ai, int[] array, int current, int next) {
this.index = index;
this.ai = ai;
this.array = array;
this.current = current;
this.next = next;
}
@Override
public void run() {
for (;;) {
if (index.get() == array.length) {
break;
}
if (ai.get() == current) {
System.out.println(Thread.currentThread().getName() + " " + array[index.getAndIncrement()]);
ai.compareAndSet(current, next);
}
}
}
}
答案 3 :(得分:0)
您需要允许您的5个线程进行通信,以便它们按严格的顺序运行。就像多米诺骨牌掉落一样,每个线程都必须等待,直到被戳破为止,然后它执行其操作并对下一个线程说:“现在可以走了”。
但是与多米诺骨牌摔倒不同,他们还必须重新站起来,以便为下一轮做好准备,最后一个多米诺骨牌必须再次击倒第一个多米诺骨牌!
我认为,这种通信的最佳内置方法是Phaser
。这是使用Phasers的实现:
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicInteger;
public final class SequencedThreads
{
private static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) {
PrintThread[] threads = new PrintThread[5];
// Create our 5 threads, each with a phaser waiting on itself
// and the previous thread
for(int i = 0; i < 5; i++) {
threads[i] = new PrintThread("T" + (i + 1));
if(i > 0)
threads[i - 1].next = threads[i].phaser;
}
// Join the last thread back to the first thread
threads[4].next = threads[0].phaser;
// Start our threads
for(PrintThread pt : threads)
pt.start();
// Trigger the first thread to print
threads[0].phaser.arriveAndDeregister();
}
private static final class PrintThread extends Thread {
Phaser phaser;
Phaser next;
public PrintThread(String name) {
super(name);
this.phaser = new Phaser(2);
}
@Override
public void run() {
while(true) {
// Block until we are poked
phaser.arriveAndAwaitAdvance();
int newCount = count.incrementAndGet();
if(newCount > 10) {
// We are done, but trigger the other threads to allow
// the JVM to exit
next.arriveAndDeregister();
return;
}
System.out.println(getName() + " -> " + newCount);
// Pick ourselves back up
phaser.register();
// Poke the next domino
next.arriveAndDeregister();
}
}
}
}
答案 4 :(得分:0)
public static void main(String... args) {
class LocalTask extends Thread {
public static final int TOTAL = 5;
private final int a;
private final int b;
private final AtomicInteger lock;
public LocalTask(int id, AtomicInteger lock) {
super("T" + id);
a = id;
b = id + TOTAL;
this.lock = lock;
}
@Override
public void run() {
try {
while (true) {
synchronized (lock) {
int count = lock.get();
String name = Thread.currentThread().getName();
if (count == a)
System.out.println(name + " -> " + a);
else if (count == b)
System.out.println(name + " -> " + b);
else
continue;
lock.incrementAndGet();
lock.notifyAll();
if (count == a)
lock.wait();
else
return;
}
}
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
AtomicInteger lock = new AtomicInteger(1);
for (int i = 1; i <= LocalTask.TOTAL; i++)
new LocalTask(i, lock).start();
}