线程间通信
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();
}