您的位置:首页 > 编程语言 > Java开发

java并发基础--等待通知模式

2015-12-02 20:46 501 查看

线程间通信

public class SynchronizedTest {

public static void main(String[] args) {

synchronized (SynchronizedTest.class){

}

m();
}

public synchronized static void m(){

}
}


在编译后的同一目录

javap -v SynchronizedTest

Last modified 2015-12-2; size 587 bytes
MD5 checksum faa56efff9ff6a17c0e345ae6503e899
Compiled from "SynchronizedTest.java"
public class concurrent.SynchronizedTest
SourceFile: "SynchronizedTest.java"
minor version: 0
major version: 50
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref          #4.#23         //  java/lang/Object."<init>":()V
#2 = Class              #24            //  concurrent/SynchronizedTest
#3 = Methodref          #2.#25         //  concurrent/SynchronizedTest.m:()V
#4 = Class              #26            //  java/lang/Object
#5 = Utf8               <init>
#6 = Utf8               ()V
#7 = Utf8               Code
#8 = Utf8               LineNumberTable
#9 = Utf8               LocalVariableTable
#10 = Utf8               this
#11 = Utf8               Lconcurrent/SynchronizedTest;
#12 = Utf8               main
#13 = Utf8               ([Ljava/lang/String;)V
#14 = Utf8               args
#15 = Utf8               [Ljava/lang/String;
#16 = Utf8               StackMapTable
#17 = Class              #15            //  "[Ljava/lang/String;"
#18 = Class              #26            //  java/lang/Object
#19 = Class              #27            //  java/lang/Throwable
#20 = Utf8               m
#21 = Utf8               SourceFile
#22 = Utf8               SynchronizedTest.java
#23 = NameAndType        #5:#6          //  "<init>":()V
#24 = Utf8               concurrent/SynchronizedTest
#25 = NameAndType        #20:#6         //  m:()V
#26 = Utf8               java/lang/Object
#27 = Utf8               java/lang/Throwable
{
public concurrent.SynchronizedTest();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1                  // Method java/lang/Object."<init>
":()V
4: return
LineNumberTable:
line 6: 0
LocalVariableTable:
Start  Length  Slot  Name   Signature
0       5     0  this   Lconcurrent/SynchronizedTest;

public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc_w         #2                  // class concurrent/SynchronizedTe
st
3: dup
4: astore_1
5: monitorenter   监视器进入获取锁
6: aload_1
7: monitorexit     监视器退出 释放锁
8: goto          16
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
16: invokestatic  #3                  // Method m:()V
19: return
Exception table:
from    to  target type
6     8    11   any
11    14    11   any
LineNumberTable:
line 10: 0
line 12: 6
line 14: 16
line 15: 19
LocalVariableTable:
Start  Length  Slot  Name   Signature
0      20     0  args   [Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 11
locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4

public static synchronized void m();
//方法修饰符
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 20: 0
}


对于同步快的实现使用了monitorenter 和 monitorexit指令 同步方法则是依靠方法修饰符上的ACC_SYNCHRONIZED 来完成 无论采用哪种方式 其本质都是对一个对象的监视器的获取 这个过程是排他的

也就是同一时刻只能有一个线程能获取到sync关键字所保护的监视器.

等待/通知机制

等待 通知机制的相关方法是任意java对象都具备的,因为这些方法定义在java.lang.Object上

方法名称描述说明
notify()通知一个在对象上等待的线程 使其从wait()方法返回 而返回的前提是该线程获取到了对象的锁
notifyAll()通知所有等待在该对象上的线程
wait()调用该方法的线程进入Waiting状态 只有等待另外线程的通知或中断才会返回 需要注意 调用wait 方法后 会释放对象的锁
wait(long)超时等待一段时间 这里的参数时间是毫秒 也就是等待长达n毫秒 如果没有通知则超时返回
wait(long,int)对于超时时间更细粒度的控制 可以达到纳秒
等待/通知机制 是指一个线程a调用了对象O的wait()方法进入等待状态 而另一个线程b调用了对象o的notify()或者NotifyAll()方法 线程a收到通知后从对象o的wait方法中返回 执行后续的操作 上述俩个线程通过对象o来完成交互 而对象上的wait和notify的关系就如同开关信号一样 用来完成等待方和通知方之间的交互

static Object lock = new Object();

static volatile boolean flag = true;

public static void main(String[] args) throws InterruptedException {

new Thread(new Wait(),"wait-thread").start();

TimeUnit.SECONDS.sleep(2);

new Thread(new Notify(),"notify-thread").start();
}

static class Wait implements Runnable {

@Override
public void run() {
//获取锁 拥有锁的Monitor
synchronized (lock){
//当条件不满足时 继续wait 同时释放了lock的锁
while (flag){
System.out.println("flag = true need wait @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

System.out.println("flag = false get here @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}

static class Notify implements  Runnable {

@Override
public void run() {
//枷锁 拥有lock的monitor
synchronized (lock){
//获取lock的锁 然后进行通知 通知时不会释放lock的锁 直到当前线程释放了 lock后 WaitThread 才从wait方法中返回
lock.notify();
System.out.println("hold lock @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
try {
TimeUnit.SECONDS.sleep(5);
flag = false;
System.out.println("hold lock @ " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


知识点:

1)使用wait() notify()或者nofityAll()时需要先对调用对象加锁

2) 调用wait()方法后 线程由running变为waiting 并将当前线程放置到对象的等待队列

3)notify()或notifyAll()方法刁永红 等待线程依旧不会从wait()方法返回 需要调用notify()或者notifyAll()的线程释放锁之后 等待线程才有机会从wait()返回

4) notify()方法将等待队列中的一个等待线程从等待队列中移动到同步队列中 而notifyAll()是将等待队列中的所有线程全部移动到同步队列 被移动的线程状态由waiting变为blocked

5)从wait()返回的前提是获得了调用对象的锁

等待/通知模式的经典范式

等待方:

1)获取对象的锁

2) 如果条件不满足 那么调用对象的wait方法 被通知后仍要检查条件、

3) 条件满足则执行对应的逻辑

伪码:

synchronized(对象){
while(条件不满足){
对象.wait()
}
对应的处理逻辑
}


通知方:

1)获得对象的锁

2)改变条件

3)通知所有等待在对象上的线程

伪码:

synchronized(对象){
改变条件
对象.notifyAll();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: