源无极

导航

 

概要:

      线程是操作系统独立的个体,线程之间的通信让他们成为一个整体,系统之间的交互性更大

大大提高CPU利用率的同时还会使程序员对各线程任务在处理的过程中进行有效的把控与监督。本章

重点学习一下几点。

1.使用wait/notify实现线程间的通信。

2.生产者、消费者模式的实现

3.方法join的使用。

4.ThreadLocal类的使用。

一、等待、通知机制

(一)、不使用等待、通知机制实现线程间的通信

实验中使用sleep和while(true)死循环来实现多个线程间的通信

package com.it.po.thread8;
import java.util.ArrayList;
import java.util.List;
public class MyList1 {
    private List<String> list =new ArrayList<>();
    public void add(){
        list.add("po");
    }
    public int size(){
        return list.size();
    }

}

 

package com.it.po.thread8;
public class MyThread1_1 extends Thread {
private MyList1 myList1;
    public MyThread1_1(MyList1 myList1) {
        this.myList1 = myList1;
    }
    @Override
    public void run() {
        super.run();
            try {
                for(int i=1;i<=10;i++) {
                    System.out.println("添加第 " + i + " 个元素");
                    myList1.add();
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


}

 

package com.it.po.thread8;
public class MyThread1_2 extends Thread {
private MyList1 myList1;
    public MyThread1_2(MyList1 myList1) {
        this.myList1 = myList1;
    }
    @Override
    public void run() {
        super.run();
        try {
            while (true){
                if(myList1.size()==5){
                    System.out.println("我 MyThread1_2 要退出了。。。");
                    throw new InterruptedException();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

 

package com.it.po.thread8;
public class Run1 {
    public static void main(String[] args){
        MyList1 myList1 = new MyList1();
        MyThread1_1 my1 = new MyThread1_1(myList1);
        MyThread1_2 my2 = new MyThread1_2(myList1);
        my1.setName("A");
        my2.setName("B");
        my1.start();
        my2.start();
    }

}

 

添加第 1 个元素
添加第 2 个元素
添加第 3 个元素
添加第 4 个元素
java.lang.InterruptedException
    at com.it.po.thread8.MyThread1_2.run(MyThread1_2.java:16)
添加第 5 个元素
我 MyThread1_2 要退出了。。。
添加第 6 个元素
添加第 7 个元素
添加第 8 个元素
添加第 9 个元素
添加第 10 个元素

 

注意:每次添加一个元素,while循环执行很多次去查询list的大小

如果如果轮询的时间小,更浪费资源,轮询时间长,有可能取不到

数据,所以需要一种机制,减少这种CPU的浪费实现多个线程的通信

这就是wait/notify

(二)、什么是等待,通知机制

先看一个案例

        厨师通过菜品传递台和服务员交互的过程,注意,这里会遇到几个问题

1.厨师做菜时间不确定,所以厨师将才放到传递台上不确定

2.服务员取菜时间取决于厨师,服务员处于等待时间。

3.厨师将菜放到传递台上,其实就是通知,服务员再将菜给客户。

4.这个过程出现了等待/通知

 

 

(三)、等待/通知机制的实现

      方法wait()作用:使当前执行代码的线程进行等待。wait()是Object类的方法,

该方法将当前线程置入  “ 预执行队列” 中并且在wait()所在的代码行处停止执行,

知道接收到通知或是被中断为止。在调用wait()之前,线程必须获得该对象的对象级别

锁,即能在同步方法或是同步块中调用wait()方法执行wait()方法后,当前线程

释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时没有

持有适当的锁,则会抛出 IllegalMonitorStateException,它是RuntimeExcetion的一个子类,因此不需要try- catch

语句进捕捉异常。

      方法notify():要在同步方法或是同步块中,即在调用前,线程也必须获得该对象的对象级别

锁。如果没有调用notify时没有持有适当的锁,也会抛出 IllegalMonitorStateException。该方法用来通知那些可能等待该对象

的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选其中一个呈wait()状态的线程

,对其发出notify()方法后,当前线程不会马上释放锁,呈wait()状态的线程也不能马上获得

锁,要等到执行notify()方法的线程将程序执行完,也就是退出 synchronized代码块后,当前线程

才会释放锁,呈wait()的状态的线程才可以获取该对象锁。当第一个获得该对象锁的wait线程运行完毕

它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态的

线程由于没有得到该对象的通知,还会继续阻塞在wait状态,一直到这个对象发出一个notify或是notifyAlla.

总结:

wait  使线程停止运行

notify 使停止的线程继续运行。

 

 

package com.it.po.thread8;
import com.sun.org.apache.xpath.internal.operations.String;
public class Run2 {
    public static void main(java.lang.String[] args){
        try {
            String string = new String();
            string.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

 

Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Object.java:502)
    at com.it.po.thread8.Run2.main(Run2.java:12)

 

 

 (一)、一直处于等待

package com.it.po.thread8;
import com.sun.org.apache.xpath.internal.operations.String;
public class Run2 {
    public static void main(java.lang.String[] args){
        try {
            String string = new String();
            System.out.println("synchronized 外面");
            synchronized (string) {
                System.out.println("wait 前面");
                string.wait();
                System.out.println("wait 后面");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

 

 

synchronized 外面
wait 前面

 

注意:wait()方法只能在同步块或是方法中。

 

 (二)、简单的等待通知

package com.it.po.thread8;
import java.util.ArrayList;
import java.util.List;
public class MyList1 {
    private List<String> list =new ArrayList<>();
    public void add(){
        list.add("po");
    }
    public int size(){
        return list.size();
    }

}

 

 

package com.it.po.thread8;
public class MyThread2_1 extends Thread {
private MyList1 myList1;
    public MyThread2_1(MyList1 myList1) {
        this.myList1 = myList1;
    }
    @Override
    public void run() {
        super.run();
        try {
            synchronized (myList1) {
                System.out.println("wait....begin");
                myList1.wait();
                System.out.println("wait....end");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

 

package com.it.po.thread8;
public class MyThread2_2 extends Thread {
private MyList1 myList1;
    public MyThread2_2(MyList1 myList1) {
        this.myList1 = myList1;
    }
    @Override
    public void run() {
        super.run();
        synchronized (myList1) {
            System.out.println("notify....begin");
            myList1.notify();
            System.out.println(" notify ....end");
        }
    }
}

 

 

package com.it.po.thread8;
public class Run3 {
    public static void main(java.lang.String[] args) throws InterruptedException {
        MyList1 myList1 = new MyList1();
        MyThread2_1 my1 = new MyThread2_1(myList1);
        MyThread2_2 my2 = new MyThread2_2(myList1);
        my1.start();
        Thread.sleep(1000);//先让wait先执行
        my2.start();
    }
}

 

 

wait....begin
notify....begin
 notify ....end
wait....end

 

 (三)、本章第一个案例解决‘

 

package com.it.po.thread8;
import java.util.ArrayList;
import java.util.List;
public class MyList2 {
    private static List<String> list =new ArrayList<>();
    public static void add(){
        list.add("po");
    }
    public static int size(){
        return list.size();
    }

}

 

 

package com.it.po.thread8;
public class MyTread3_1 extends Thread {
private Object lock;
    public MyTread3_1(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        super.run();
        try {
            synchronized (lock) {
              for(int i=1;i<=10;i++){
                  MyList2.add();
                  if(MyList2.size()==5){
                      lock.notify();
                      System.out.println("发唤醒通知。。。");
                  }
                  System.out.println("添加了第 "+i +" 个元素");
                  Thread.sleep(1000);
              }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

 

package com.it.po.thread8;
public class MyTread3_2 extends Thread {
private Object lock;
    public MyTread3_2(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        super.run();
            synchronized (lock) {
                      try {
                          if(MyList2.size()!=5) {
             System.out.println("wait 通知。。。begin "+MyList2.size());
                              lock.wait();
             System.out.println("wait通知。。。end");
                          }
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
}

 

 

package com.it.po.thread8;
public class Run4 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();
        MyTread3_1 my1 = new MyTread3_1(o);
        MyTread3_2 my2 = new MyTread3_2(o);
        my1.start();
        Thread.sleep(100);//先让wait先执行
        my2.start();
    }
}
添加了第 1 个元素
添加了第 2 个元素
添加了第 3 个元素
添加了第 4 个元素
发唤醒通知。。。
添加了第 5 个元素
添加了第 6 个元素
添加了第 7 个元素
添加了第 8 个元素
添加了第 9 个元素
添加了第 10 个元素
wait 通知。。。begin 10

 

 

结论:

1.发通知之后先执行 完 MyTread3_1的代码才会释放锁。

 2.如果有多个线程等待同一个锁,notify()只是通知其中一个线程。

补充:

       notifyAll是可以使所有正在等待队列中的等待同一资源的“全部”线程从待带中状态退出,

进入可运行状态。此时优先级最高的优先执行,但可能是随机执行。这取决于虚拟机JVM的

实现。

 

 

 Runnable(可运行)状态。

Running(运行)状态

        Runnable(可运行)状态。和Running(运行)状态 可以相互切换,

如有可能线程A运行的时候Running,此时有优先级高的线程抢到线程

此时,A线程由Running转到Runnable状态。

线程进入Runnable状态分为以下五种

1.调用sleep()后,超过了指定的休眠时间

2.线程调用的阻塞IO已经返回,阻塞的方法执行完毕。

3.线程成功获得试图同步的监视器

4.线程正在等待某一个通知,有线程发出了通知

5.处于挂起状态的线程调用了resume恢复方法。

         Bolcked是阻塞的意思,例如遇到一个阻塞的IO操作,此时CPU处于空闲状态

可能会转而把时间碎片分配给其他线程,这时可以称为暂停状态,Bolcked状态

结束后进入Runnable状态,等待系统重新分配资源。

出现阻塞有以下五种情况

1.线程调用sleep(),主动放弃占用的处理器资源。

2.线程调用了阻塞式IO方法,在该方法返回前,该线程被阻塞

3.线程试图获得一个同步监视器,但该同步监视器被其他线程所持有

4.线程等待某一个通知

5.线程调用 了suspend()方法将该线程挂起,此方法容易导致死锁,尽量

避免使用该方法。

run()方法执行结束后进行销毁阶段,整个线程执行完毕

               每一个锁对象都有两个队列,一个是就绪队列,另一个就是阻塞队列,

就绪队列存储了将要获得锁的线程,阻塞队列是被阻塞的线程。

一个线程被唤醒后才会进入就绪队列,等待CPU调度,反之,一个线程

wait后就会进入阻塞队列,等待下一次被唤醒。

 

 

 

 

 

 

 

 

 

 

 

posted on 2019-11-19 23:14  源无极  阅读(236)  评论(0)    收藏  举报