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) | 对于超时时间更细粒度的控制 可以达到纳秒 |
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(); }
相关文章推荐
- 搭建Struts2开发环境
- 多继承有什么坏处,为什么java搞单继承,接口为什么可以摈弃这些坏处
- Struts2 DomainModel、ModelDriven接收参数
- CAS client客户端的配置,使用java config的方式
- spring容器DI依赖注入对象<三>
- Java集合类
- java常用的几种线程池比较
- Java基础概要(二)
- Java基本知识(连载)-深入面向对象
- Java基础概要(一)
- Java--length,length(),size()区别
- spring 构造方法注入和setter方法注入的XML表达
- java发送邮件
- Java反射研究和实践
- Java代理和动态代理机制分析和应用
- 用Ant实现Java项目的自动构建和部署
- Java的注释说明
- 给Java程序猿们推荐一些值得一看的好书
- Java链表的一些操作:
- shiro和Spring整合使用注解时没有执行realm的doGetAuthorizationInfo回调方法的解决