Reputation: 21
I have 4 threads. Each prints the given letter x times every x second. The task is to start 3 threads at once and the 4th one when at least one of the previous threads is finished. I don't know how to notify this last thread to run in due time.
Upvotes: 2
Views: 673
Reputation: 2602
To use semaphores to wait until one of 3 threads finishes, before starting a 4th different thread, you can initialise the semaphore to 0
. Before running the 4th thread, you acquire the semaphore, but cannot, since it is 0
. When one of the 3 threads finishes, it releases the semaphore, allowing the 4th thread to run.
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
public class Main {
public static void main(String[] args) throws InterruptedException {
Semaphore sem = new Semaphore(0);
char[] letters = {'a', 'b', 'c'};
List<Thread> threadList = new ArrayList<>();
for (int threadNum = 0; threadNum < 3; threadNum++) {
int finalThreadNum = threadNum;
Thread t = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.print(letters[finalThreadNum]);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
sem.release();
});
threadList.add(t);
t.start();
}
Thread lastThread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.print('d');
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
sem.acquire();
lastThread.start();
for (Thread t : threadList) {
t.join();
}
lastThread.join();
}
}
Upvotes: 0
Reputation: 568
Depends what is your assignment? Do you care about the work? The threads?
You can always "hack it" by making thread pool with three threads and give it four tasks:
ExecutorService es = Executors.newFixedThreadPool(3);
es.execute(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("A");
try {Thread.sleep(100);} catch (InterruptedException e) {}
}
});
es.execute(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("B");
try {Thread.sleep(100);} catch (InterruptedException e) {}
}
});
es.execute(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("C");
try {Thread.sleep(100);} catch (InterruptedException e) {}
}
});
es.execute(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("D");
try {Thread.sleep(100);} catch (InterruptedException e) {}
}
});
Or alternatively make the threads yourself (D thread first), and at the end of the work of each (ABC) ask if D is running, if not, start it.
There is so many solutions to this with wildly different complexity, and whether they are alright depends solely on your context.
Upvotes: 0
Reputation: 44130
CompletableFutures are the modern way to accomplish this in an expressive way.
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
CompletableFuture<Void> a = CompletableFuture.runAsync(() -> print("A"), executor);
CompletableFuture<Void> b = CompletableFuture.runAsync(() -> print("B"), executor);
CompletableFuture<Void> c = CompletableFuture.runAsync(() -> print("C"), executor);
CompletableFuture.anyOf(a, b, c).thenRunAsync(() -> print("D"), executor);
}
private static void print(String taskName) {
for (int i = 0; i < 10_000; ++i) {
System.out.println("Task " + taskName + ": " + i);
}
}
Run A, B and C, then when any one of them is done, run D.
You can simplify it even further by removing the executor and just using the fork/join pool, but it doesn't work so well in a self-contained example because they're daemon threads; the program will just end very quickly before it does much of anything.
Upvotes: 3
Reputation: 27115
What if the 4th thread's semaphore.acquire() happens before the other 3 threads' acquires...
You could use a CountDownLatch
for that. Building on @Ryan 's answer:
public static void main(String [] args) throws IOException {
Semaphore semaphore = new Semaphore(3, true);
CountDownLatch okToStartTaskD = new CountDownLatch(3);
Runnable a = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
okToStartTaskD.countDown();
...
}
...
}
};
Runnable b = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
okToStartTaskD.countDown();
...
}
...
}
};
...
ExecutorService exec = Executors.newFixedThreadPool(5);
exec.execute(a);
exec.execute(b);
exec.execute(c);
try {
okToStartTaskD.await();
}
catch (InterruptedException ex) {
ex.printStackTrace();
...should never get here, but what if?...
}
exec.execute(d);
}
Upvotes: -1
Reputation: 1760
You can use a Semaphore
. The first 3 threads will take all the permits and when the first one releases a permit, the 4th will pick it up and run.
public static void main(String [] args) throws IOException {
Semaphore semaphore = new Semaphore(3, true);
Runnable a = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
for (int i = 0; i < 50; i++) {
System.out.println("A");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
};
Runnable b = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
for (int i = 0; i < 50; i++) {
System.out.println("B");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
};
Runnable c = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
for (int i = 0; i < 50; i++) {
System.out.println("C");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
};
Runnable d = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("D");
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
};
ExecutorService exec = Executors.newFixedThreadPool(5);
exec.execute(a);
exec.execute(b);
exec.execute(c);
exec.execute(d);
}
Upvotes: -1