В этом примере мы хотим, чтобы эти действия происходили в следующем порядке:
- порождает поток T1, T2 и T3 и переводит все три в спящий режим на общем ресурсе
- изменить флаг volatile и spawn T4
- T4 входит в критическую секцию, пробуждает один спящий поток, а затем выходит
- после получения уведомления, каждый поток уведомляет ровно один раз и затем выходит
Приведенный выше код является почти правильным, но поскольку метод main
выполняется "основным" потоком, мы должны убедиться, что потоки T1, T2, T3 go находятся в состоянии ожидания до нерестовая нить T4. Мы можем сделать это, используя семафор. Поток main
будет блокироваться при вызове semaphore.acquire()
до тех пор, пока все потоки не будут в правильном состоянии.
Примечание , что из-за гонки, в приведенном выше коде поток T1 может войти в критической секции после флаг установлен в ложь - поэтому мы потеряем контроль над порядком действий.
См. код обновления ниже, он выведет что-то вроде
main thread starts
Thread-2 enters with status true
Thread-2 goes into waiting stage
Thread-1 enters with status true
Thread-1 goes into waiting stage
Thread-0 enters with status true
Thread-0 goes into waiting stage
Thread-3 enters with status false
Thread-3 calling the notify method
Thread-3 terminates
main thread terminates
Thread-2 wakes up
Thread-2 terminates
Thread-1 wakes up
Thread-1 terminates
Thread-0 wakes up
Thread-0 terminates
код
package sample;
import java.util.concurrent.Semaphore;
public class MultithreadingTest {
public static void main(String[] args) throws Exception {
System.out.println("main thread starts");
SharedResource sr = new SharedResource();
// to make thread to go in waiting state
sr.isWaiting = true;
Thread t1 = new Thread(sr);
Thread t2 = new Thread(sr);
Thread t3 = new Thread(sr);
t1.start();
t2.start();
t3.start();
// let the main thread wait, until all 3 threads reach the checkpoint
sr.numWaiting.acquire(3);
sr.isWaiting = false; // to directly call the notify() based on condition
Thread t6 = new Thread(sr);
t6.start();
System.out.println("main thread terminates");
}
}
class SharedResource implements Runnable {
// keep a count of how many threads entered the critical section
// needed by the main thread to know when to start the "wake-up" thread
Semaphore numWaiting = new Semaphore(0);
volatile boolean isWaiting;
public void run() {
System.out.println(Thread.currentThread().getName() + " enters with status " + this.isWaiting);
synchronized (this) {
try {
if (this.isWaiting) {
System.out.println(Thread.currentThread().getName() + " goes into waiting stage");
numWaiting.release(1);
wait(); // Release the lock so other threads can acquire it
System.out.println(Thread.currentThread().getName() + " wakes up");
} else {
System.out.println(Thread.currentThread().getName() + " calling the notify method");
}
notify(); // Notifying the random thread which is in waiting state to resume again
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " terminates");
}
}
}