线程间通信使用wait()和notify()方法,这两个方法都是Object类的方法,调用时线程必须持有该对象的对象级别锁,如果调用时没有持有适当的锁,会抛出IllegalMonitorStateException。
- 调用wait()方调用线程的interrupt()方法会出现InterruptedException异常
- wait(long)方法的功能是等法后,当前线程会释放持有的锁进入等待状态
- notify()方法可以唤醒一个因调用了wait操作而处于等待状态中的线程,使其进入就绪状态,加入争抢锁的队列,notify()方法仅通知“一个”线程,且不会立即释放当前锁。
- 当线程呈wait()状态时,待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒
1. 用法示例
public class Thread1 extends Thread {
private Object lock;
public Thread1(Object lock) {
this.lock = lock;
}
@Override
public void run() {
try {
synchronized (lock){
System.out.println("开始 wait time="+System.currentTimeMillis());
lock.wait();
System.out.println("结束 wait time="+System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Thread2 extends Thread {
private Object lock;
public Thread2(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
System.out.println("开始 notify time=" + System.currentTimeMillis());
lock.notify();
System.out.println("结束 notify time=" + System.currentTimeMillis());
}
}
}
public class TestMain {
public static void main(String[] args) throws InterruptedException {
Object lock=new Object();
Thread1 thread1=new Thread1(lock);
Thread2 thread2=new Thread2(lock);
thread1.start();
Thread.sleep(3000);
thread2.start();
}
}
2. 等待wait的条件发生变化
public class Add extends Thread{
private List list;
private Object lock;
public Add(List list,Object lock) {
this.list = list;
this.lock=lock;
}
@Override
public void run() {
synchronized (lock){
list.add("1");
lock.notifyAll();
}
}
}
public class Sub extends Thread{
private List list;
private Object lock;
public Sub(List list, Object lock) {
this.list = list;
this.lock = lock;
}
@Override
public void run() {
synchronized (lock){
try {
if (list.size()==0){
System.out.println("wait begin "+Thread.currentThread().getName());
lock.wait();
System.out.println("wait end "+Thread.currentThread().getName());
}
list.remove(0);
System.out.println("list size="+list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
String lock="";
List list=new ArrayList();
Add add=new Add(list,lock);
add.setName("add");
Sub sub1=new Sub(list,lock);
sub1.setName("sub1");
Sub sub2=new Sub(list,lock);
sub2.setName("sub2");
sub1.start();
sub2.start();
Thread.sleep(1000);
add.start();
}
wait begin sub1
wait begin sub2
wait end sub2
Exception in thread "sub1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
list size=0
wait end sub1
at java.util.ArrayList.rangeCheck(ArrayList.java:657)
at java.util.ArrayList.remove(ArrayList.java:496)
at com.yjj.chapter03.test02_notify2.Sub.run(Sub.java:29)
当sub1去删除时,sub2已经把list里的值删了,此时list的size是0,再去删除就会报索引越界错误,解决办法就是if改为while
3. 生产者消费者实现
public class Consumer {
private String lock;
public Consumer(String lock) {
this.lock = lock;
}
public void getValue() {
synchronized (lock) {
try {
// Thread.sleep(1000);
if (ValueObject.value.equals("")) {
System.out.println(Thread.currentThread().getName()+"waiting");
lock.wait();
// System.out.println("get的值是" + ValueObject.value);
}
ValueObject.value = "";
lock.notify();
System.out.println(Thread.currentThread().getName()+"notify");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Productor {
private String lock;
public Productor(String lock) {
this.lock = lock;
}
public void setValue(){
try{
// Thread.sleep(1000);
synchronized (lock){
if (!ValueObject.value.equals("")){
System.out.println(Thread.currentThread().getName()+"waiting");
lock.wait();
}
String value=System.currentTimeMillis()+"_"+System.nanoTime();
// System.out.println("set的值是"+value);
ValueObject.value=value;
lock.notify();
System.out.println(Thread.currentThread().getName()+"notify");
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadP extends Thread {
private Productor productor;
public ThreadP(Productor productor) {
this.productor = productor;
}
@Override
public void run() {
while (true) {
productor.setValue();
}
}
}
public class ThreadC extends Thread {
private Consumer consumer;
public ThreadC(Consumer consumer) {
this.consumer = consumer;
}
@Override
public void run() {
while (true) {
consumer.getValue();
}
}
}
public class TestMain {
public static void main(String[] args) throws InterruptedException {
String lock=new String("");
Productor productor=new Productor(lock);
Consumer consumer=new Consumer(lock);
ThreadC threadC=new ThreadC(consumer);
threadC.setName("C");
ThreadP threadP=new ThreadP(productor);
threadP.setName("p");
threadC.start();
threadP.start();
}
}
4. join()方法
@Override
public void run() {
try {
int second=(int) (Math.random()*1000);
Thread.sleep(second);
System.out.println("线程执行完了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread=new MyThread();
myThread.start();
//Thread.sleep(??); //睡多少秒好呢,我们可以使用join来实现
myThread.join();
System.out.println("我想等myThread执行完毕后再执行");
}
join()方法的作用是使当前所在的线程无限阻塞,直到调用join方法的线程执行完毕销毁后再继续执行。
方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是“对象监视器”原理作为同步。
5. join()遇到interrupt()
线程B启动一个线程A并join到线程A,等线程A执行完才继续执行,线程A做一个耗时巨长的事情,线程C执行线程B interrupt(),此时线程B会报异常,,,算了,上代码吧talk is cheap show my code
public class ThreadA extends Thread{
@Override
public void run() {
for (int i=0;i
public class ThreadB extends Thread{
@Override
public void run() {
try {
ThreadA threadA=new ThreadA();
threadA.start();
threadA.join();
System.out.println("线程B执行完毕");
} catch (InterruptedException e) {
System.out.println("线程B被中断了");
e.printStackTrace();
}
}
}
public class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB) {
this.threadB = threadB;
}
@Override
public void run() {
threadB.interrupt();
}
}
public class TestMain {
public static void main(String[] args) throws InterruptedException {
ThreadB threadB=new ThreadB();
threadB.start();
ThreadB.sleep(1000);
ThreadC threadC=new ThreadC(threadB);
threadC.start();
}
}
java.lang.InterruptedException
线程B被中断了
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
at java.lang.Thread.join(Thread.java:1326)
at com.yjj.chapter03.test05_join_interrupt.ThreadB.run(ThreadB.java:16)
可以看到,在我们调用join方法时,编译器要求我们必须要处理一个检查异常,以防止join时该线程被中断
6.join(long)与sleep(long)的区别
join(long)在内部其实是使用wait(long)来实现的,所以你懂得,wait是释放锁的,而sleep是不释放锁的。
7. ThreadLocal
感觉也应该单拿出来讲