public class Main2 {
public static void main(String[] args) {
new Test2().start();
new Test2().start();
}
}
class Test2 extends Thread {
@Override
synchronized public void run() {
try {
System.out.println("begin wait");
wait();
} catch (Exception ex) {
}
}
}
Как фактический результат запущения теста: начните ожидают, начинаются, ожидают, два раза от двух потоков. Контраст по отношению к ожидаемому результату: начните ожидают, только один раз от одного из двух потоков, потому что ожидают () называют в синхронизируемом выполнении () методом. Почему мог назвать к ожиданию Объекта () синхронизацию потока повреждения?
Thans много!
public class Main3 {
public static void main(String[] args) {
Test3 t = new Test3();
new Thread(t).start();
new Thread(t).start();
}
}
class Test3 implements Runnable {
synchronized public void run() {
try {
System.out.println("begin wait");
wait();
} catch (Exception ex) {
}
}
}
@akf и @Sean Owen
Спасибо за Ваши ответы. Извините за мою ошибку теперь я изменил код для размещения синхронизации в выполнение того же объекта (), результат остался: начните ожидают, начинаются, ожидают, два раза.
@akf
ожидание выпустит блокировку, которые синхронизируются, захватил и будет повторно получен, после того как поток уведомляется.
Вы могли уточнить немного?
Test2
будет синхронизироваться на другом мониторе. sleep
, а не wait
. wait
освободит блокировку, которую захватил synchronized
, и будет снова захвачена, как только поток получит уведомление. Обратите внимание, что для корректной работы вашего теста вам понадобится блокировка на общем объекте. Если вы хотите увидеть wait
в действии, я создал простое приложение, в котором будет всплывать фрейм с кнопкой "Notify". Будут запущены два потока, которые ждут общий объект и, в свою очередь, получают уведомление при нажатии кнопки.
public static void main(String[] args)
{
final Object lock = new Object();
final JFrame frame = new JFrame("Notify Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Notify");
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent evt) {
synchronized(lock) {
lock.notify();
}
}
});
frame.add(button);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
frame.setVisible( true );
}
});
new Thread(new Runnable() {
public void run() {
synchronized(lock) {
try {
System.out.println("1. starting");
lock.wait();
System.out.println("1. step 1");
lock.wait();
System.out.println("1. step 2");
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
synchronized(lock) {
try {
System.out.println("2. starting");
lock.wait();
System.out.println("2. step 1");
lock.wait();
System.out.println("2. step 2");
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}).start();
}
Для простого объяснения wait
, JavaDoc всегда является хорошим местом для начала:
Заставляет текущий поток ждать, пока другой поток не вызовет метод notify() или метод notifyAll() для этого объекта. Другими словами, этот метод ведет себя точно так же, как если бы он просто выполнял вызов wait(0).
Текущий поток должен владеть монитором этого объекта. Поток освобождает право собственности на монитор и ждет, пока другой поток не уведомит потоки, ожидающие на мониторе этого объекта, о необходимости проснуться либо вызовом метода notify, либо методом notifyAll. После этого поток ждет, пока он снова не сможет получить право собственности на монитор, и возобновляет выполнение.
У вас есть два разных объекта Test2. Синхронизированные методы блокируют объект. Они не получают одну и ту же блокировку, поэтому не следует печатать дважды.
простой пример, который может вам помочь:
public class test {
public static void main(String[] args) {
Prova a=new Prova();
new Test2(a).start();
new Test2(a).start();
}
}
class Prova{
private boolean condition;
public void f(){
while(condition){
//Thread.currentThread Returns a reference to the currently executing thread object.
//Thread.getName() return name Thread
System.out.println(Thread.currentThread().getName()+" begin wait");
try{
wait();
}catch(InterruptedException c){return;}
}
System.out.println(Thread.currentThread().getName()+" first to take the mutex");
condition=true;
}
}
class Test2 extends Thread {
private Prova a;
private static boolean condition;
public Test2(Prova a){
this.a=a;
}
@Override
public void run() {
synchronized(a){
try {
a.f();
} catch (Exception ex) {
}
}
}
}
в этом случае два потока синхронизируют объект, первый принимает сообщение об освобождении блокировки, второй ждет. в этом примере используется переменная condition
резюме к механизму wait/notify:
1)текущий поток достигает синхронизированного блока кода одного объекта, который содержит вызов wait(), он конкурирует с другими потоками за блокировку (монитор объекта), как победитель он выполняет блок, пока не встретит вызов wait().
2)вызывая wait(), текущий поток освобождает блокировку для других конкурирующих потоков, затем останавливает выполнение, ожидая уведомления от другого потока, которому удалось получить блокировку.
JavaDoc:
Поток становится владельцем монитора объекта одним из трех способов:
- путем выполнения синхронизированного экземпляра метода этого объекта.
- Путем выполнения тела синхронизированного оператора который синхронизируется на объекте.
- Для объектов типа Class, путем выполнения синхронизированного статического метода этого класса.
3)другой поток достигает очередного синхронизированного блока кода того же объекта, который содержит вызов notify/notifyAll(), он конкурирует с другими потоками за блокировку, как победитель он выполняет блок до завершения вызова notify/notifyAll(). Он освободит блокировку либо вызовом wait(), либо в конце выполнения блока.
4)при получении notify/notifyAll(), текущий поток конкурирует за блокировку, как победитель продолжает выполнение с того места, где остановился.
простой пример:
public class Main3 {
public static void main(String[] args) {
Test3 t = new Test3();
new Thread(t).start();
new Thread(t).start();
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
t.testNotifyAll();
}
}
class Test3 implements Runnable {
synchronized public void run() {
System.out.println(Thread.currentThread().getName() + ": " + "wait block got the lock");
try {
wait();
} catch (Exception ex) {
}
System.out.println(Thread.currentThread().getName() + ": " + "wait block got the lock again");
try {
Thread.sleep(1000);
} catch (Exception ex) {
}
System.out.println(Thread.currentThread().getName() + ": " + "bye wait block");
}
synchronized void testNotifyAll() {
System.out.println(Thread.currentThread().getName() + ": " + "notify block got the lock");
notifyAll();
System.out.println(Thread.currentThread().getName() + ": " + "notify sent");
try {
Thread.sleep(2000);
} catch (Exception ex) {
}
System.out.println(Thread.currentThread().getName() + ": " + "bye notify block");
}
}
вывод:
Thread-0(или 1): wait block got the блокировку
Thread-1(или 0): блок ожидания получил блокировку
main: notify block got the блокировка
main: уведомление отправлено
main: пока блок уведомления
Thread-0(или 1): блок ожидания снова получил блокировку
Thread-0(или 1): пока блок ожидания
Thread-1(или 0): блок ожидания снова получил блокировку
Thread-1(или 0): пока wait block