线程间的通信

来源:互联网 发布:软件项目总结 ppt 编辑:程序博客网 时间:2024/05/18 01:16

线程是操作系统中独立的个体,但是这些个体如果不经过特殊的处理就不能成为一个整体。线程间的通信就是成为整体的必用方案之一,可以说,使用线程间通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对各个线程任务处理的过程中进行有效的把控和监控。

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

方法wait() 的作用是使当前执行代码的进程进行等待,wait()方法是Object类的方法,该方法将当前线程置入“预执行队列”中,并在wait()所在的代码行处停止执行,直到接到通知或者被中断为止。在调用wait()前,线程必须获得该对象的对象级别的锁,即只能在同步方法块或者同步方法中调用wait(),在执行wait()后,当前线程释放锁。在wait()返回前,线程和其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则会抛出异常。

方法notify()也要在同步方法或者同步代码块中调用。该方法用来通知那些可能等待该对象的对象锁的其他线程。需要说明的是,在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也不会马上获得该对象锁,要等到执行notify方法的线程将程序都执行完,也就是退出synchronized代码块后,当前线程才会释放锁。

首先看下面的代码:

public class Test {    public static void main(String[] args) {        String lock = new String("");        System.out.println("同步代码块前");        try {            synchronized (lock) {                System.out.println("进入同步代码块,开始等待。。。。。");                lock.wait();                System.out.println("等待结束。。。");            }        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

执行结果如下:
这里写图片描述

通过运行结果可以看到,代码执行到wait后就没再继续执行,如何使程序继续执行,就需要notify()方法。
创建以下两个类:Thread1.java和Thread2.java
Thread1.java代码如下:

public class MyThread1 extends Thread{    private Object lock;    public MyThread1(Object lock){        super();        this.lock = lock;    }    @Override    public void run() {        try {            synchronized(lock){                System.out.println("开始等待时间:" + System.currentTimeMillis());                lock.wait();                System.out.println("结束等待时间:" + System.currentTimeMillis());            }        } catch (Exception e) {            // TODO: handle exception        }    }}

Thread2.java代码如下:

public class MyThread2 extends Thread{    private Object lock;    public MyThread2(Object lock){        super();        this.lock = lock;    }    @Override    public void run() {        try {            synchronized(lock){                System.out.println("开始唤醒时间:" + System.currentTimeMillis());                lock.notify();                System.out.println("结束唤醒时间:" + System.currentTimeMillis());            }        } catch (Exception e) {            // TODO: handle exception        }    }}

添加测试代码Test.java:

public class Test {    public static void main(String[] args) {                try {            Object lock = new Object();            MyThread1 t1 = new MyThread1(lock);            t1.start();            Thread.sleep(3000);            MyThread2 t2 = new MyThread2(lock);            t2.start();        } catch (Exception e) {            // TODO: handle exception            e.printStackTrace();        }    }}

运行测试代码,结果如下:
这里写图片描述

通过运行结果可以看出,三秒后,线程被唤醒继续运行。

知道了wait和notify的使用,如何通过两个方法进行线程间通信呢?
接下来我们完成以下几个实验。
首先创建一个MyLisy.java类,用于存放共享变量,代码如下:

import java.util.ArrayList;import java.util.List;public class Mylist {    private static List<Object> list = new ArrayList<>();    public static void add(){        list.add("baifu");    }    public static int size(){        return list.size();    }}

然后创建一个ThreadWait.Java类,代码如下:

public class ThreadWait extends Thread{    private Object lock;    public ThreadWait(Object lock) {        super();        this.lock = lock;    }    @Override    public void run() {        try {            synchronized (lock) {                if(Mylist.size() != 5){                    System.out.println("开始等待"+System.currentTimeMillis());                    lock.wait();                    System.out.println("结束rhaou等待"+System.currentTimeMillis());                }            }        } catch (Exception e) {            // TODO: handle exception            e.printStackTrace();        }    }}

再创建ThreadNotify.java类,代码如下:

public class ThreadNotify  extends Thread{    private Object lock;    public ThreadNotify(Object lock) {        super();        this.lock = lock;    }    @Override    public void run() {        try {            synchronized (lock) {                for(int i = 0; i < 10; i++){                    Mylist.add();                    if(Mylist.size() == 5){                        System.out.println("通知结束等待");                        //Thread.sleep(1000);                        lock.notify();                    }                    System.out.println("添加了" + (i + 1) + "个元素");                }            }        } catch (Exception e) {            // TODO: handle exception            e.printStackTrace();        }    }}

创建测试类,Test.java,代码如下:

public class Test {    public static void main(String[] args) {        Mylist service = new Mylist();        ThreadWait a = new ThreadWait(service);        a.start();        ThreadNotify b = new ThreadNotify(service);        b.start();    }}

运行测试类,结果如下:
这里写图片描述

从上述结果来看,notify()方法执行之后并不立即释放锁,而是等待synchronized代码块执行完后才释放锁。

上述notify()方法只能随机唤醒一个线程,如果多个线程需要唤醒,则需要使用notifyAll()方法。

注意事项

在线程中我们使用wait和notify进行线程间的通信,上述只是一个简单的案例实现,在实际用中我们还需要有许多要做的事情,比如:
方法wait锁释放与notify锁不释放问题
Wait遇上interrupt方法
如何做到通知一个线程或者多个线程
通知过早问题
等待wait的条件发生变化

“生产者/消费者”模式

另外,等待/通知模式组经典的案例就是“生产者/消费者”模式。但在此模式上使用有几种变形,还有些小的注意事项,但是原理都是基于wait/notify模式

线程间变量共享

关于线程间变量共享可以使用private static 变量的形式,所有的线程都使用同一个private static变量。如果想实现没一个线程都有自己的变量,JDK中提供的类ThreadLocal可以解决这个问题。

类ThreadLocal主要解决的问题是每个线程都绑定自己值,可将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。

注:以上内容根据《java多线程编程核心技术》一书整理编写,感谢作者的辛勤劳作。

原创粉丝点击