线程间通信

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();
}