线程间通讯
2015-08-17 11:07
363 查看
线程间通讯
多个线程在处理同一个资源,但是任务却不同时,需要使用线程间通讯。为了实现线程间通信,需要借助Object类提供的wait()、notify()、notifyAll()三个方法。这三个方法不属于Thread,而是Object。这三个方法必须有同步监视器对象调用。
因为监视器对象可以是任意的object对象,任意的对象都能够调用的方法可定得写在object中。
对于synchronized修饰的方法,因为同步监视器默认就是this,所以可以在同步方法中直接调用这三个方法。
对于synchronized修饰的同步代码块。同步器是synchronized后面括号里的对象,所以必须使用括号里的对象调用这三个方法。
这三个方法的作用:
wait():是当前的进程等待,直到其他线程调用该同步器的notify()方法或notifyAll()来唤醒线程。调用wait()方法的线程会释放对该同步器的锁定。
notify(): 唤醒在此同步器上的等待的单个线程。如果多个线程在同步器上等待,则会随机的唤醒一个。只有当前线程放弃了对同步器的锁定后,才可以执行被唤醒的线程。
notifyAll():唤醒在此同步器上的所有线程。只有当前线程放弃了对同步器的锁定后,才可以执行被唤醒的线程。
下面实现了一个生产消费的流程,当生产者生产完成后通知消费者消费,当消费了之后通知生产者接着生产。生产和消费交替进行。
public class ProducerConsumer { public static void main(String[] args) { Resouse account = new Resouse() ; Producer producer = new Producer(account) ; Consumer consumer = new Consumer(account); //生产线程 Thread t1 = new Thread(producer); //消费线程 Thread t3 = new Thread(consumer); t1.start(); t3.start(); } } class Resouse { //生产的资源,每次生产就加1,消费就输出数值。 int sum = 0 ; //记录生产消费的状态 Boolean flag = false; //生产方法,由于save()和out()方法同时都会操作资源所以加锁 //根据flag判断状态,如果完成了生产,线程阻塞(通过wait 使线程等待,并释放锁)。当out方法唤醒线程继续向下执行。 //如果没有完成生产,直接生产,并将状态改为完成生产,唤醒消费线程。 synchronized void save(){ if(flag){ try { //线程阻塞 wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //唤醒在this上等待的方法 notifyAll(); flag = true ; sum++ ; System.out.println("producer"+sum); } //消费方法:当生产完成后,生产线程会通知消费线程。消费线程获得锁后,进行消费 //消费完成后给该状态,通知生产进程进行生产。 synchronized void out(){ if(!flag){ try { //线程阻塞 wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else //唤醒在this上等待的方法 notifyAll(); flag = false ; System.out.println("consumer<<<"+sum); } } //生产线程的Target,调用一百次生产方法 class Producer implements Runnable{ Resouse account ; public Producer(Resouse account ){ this.account = account ; } public void run(){ for(int i=0;i<100;i++){ account.save(); } } } //消费线程的Target,调用一百次消费方法。 class Consumer implements Runnable{ Resouse account ; public Consumer(Resouse account ){ this.account = account ; } public void run(){ for(int i=0;i<100;i++){ account.out(); } } }
上面的程序使用wait()和notify()进行控制。对于生产线程而言,当程序进入save()方法后,如果flag是true,则表明已经生产了,程序调用wait()方法阻塞线程;否则程序向下执行生产操作,当生产完成了将flag设置成true,再盗用notifyAll()唤醒其他被阻塞的线程。消费线程就开始执行。
使用Conditon控制线程通讯
当程序不实用synchronized关键子来保证同步,而直接使用Lock对像来控制同步。系统中不存在隐式的同步监视器,不能使用wait() 、notify()、notifyAll()方法进行线程通讯。当使用Lock对象保证线程同步时,可以使用Condition类来进行线程通讯。
Conditon提供可跟上面功能相近的方法
await():类是于隐式同步器上的wait()方法,导致当前线程等待,知道其他线程调用该Conditon的signal()方法或是signalAll()方法来唤醒该线程。
signal():唤醒在当前Lock上等待的单个线程,如果在Lock对象上有多个线程等待,则会随机的唤醒一个。只有当前线程放弃了对Lock对象的锁定后,才能执行被唤醒的方法。
signalAll():唤醒在当前Lock上等待的所有线程。只有当前线程放弃了对Lock对象的锁定后,才能执行被唤醒的方法。
public class ProducerConsumer { public static void main(String[] args) { Resouse account = new Resouse() ; Producer producer = new Producer(account) ; Consumer consumer = new Consumer(account); //生产线程 Thread t1 = new Thread(producer); Thread t2 = new Thread(producer); Thread t3 = new Thread(producer); //消费线程 Thread t4 = new Thread(consumer); t1.start(); t3.start(); t2.start(); t4.start(); } } class Resouse { //new 一个Lock的实例 Lock lock = new ReentrantLock(); //同过lock获得Conditon Condition conditon = lock.newCondition(); Condition con_producer = lock.newCondition(); Condition con_consuner = lock.newCondition(); int sum = 0 ; Boolean flag = false; public void save(){ lock.lock(); try{ while(flag){ try { con_producer.await(); } catch (InterruptedException e) { e.printStackTrace(); } } con_consuner.signal(); flag = true ; sum++ ; System.out.println("producer"+sum); }finally{ lock.unlock(); } } public void out(){ lock.lock(); try{ while(!flag){ try { con_consuner.await(); } catch (InterruptedException e) { e.printStackTrace(); } } con_producer.signal(); flag = false ; System.out.println("consumer<<<"+sum); }finally{ lock.unlock(); } } } class Producer implements Runnable{ Resouse account ; public Producer(Resouse account ){ this.account = account ; } public void run(){ for(int i=0;i<100;i++){ account.save(); } } } class Consumer implements Runnable{ Resouse account ; public Consumer(Resouse account ){ this.account = account ; } public void run(){ for(int i=0;i<100;i++){ account.out(); } } }
停止线程
当线程任务中的循环结束的时候,线程就停止了。只要控制循环就可以结束线程。控制循环通常用定义标记来完成。
public class StopThread implements Runnable{ Boolean flag = true ; public static void main(String args []){ StopThread s = new StopThread(); Thread t = new Thread(s) ; Thread t2 = new Thread(s) ; t.start(); t2.start(); //主线程sleep 1秒后 设置 flag为false。子线程停止 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } s.setFlag(); } public void run(){ while(flag){ System.out.println(Thread.currentThread().getName()); } } void setFlag(){ flag = false ; } }
如果线程处于冻结状态,无法读取标记如何结束
可使用interrupt()方法将处于冻结状态的线程强行恢复过来。强行打断会发生InterruptedException,处理Excepiton是将flag设置为false。public class StopThread implements Runnable{ Boolean flag = true ; public static void main(String args []){ StopThread s = new StopThread(); Thread t = new Thread(s) ; t.start(); //主线程sleep 1秒后 打断线程t。子线程停止 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } t.interrupt(); } public synchronized void run(){ while(flag){ try { wait(); } catch (InterruptedException e) { //处理异常,将flag设置成false flag = false ; e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"over"); } }
相关文章推荐
- C#线程间不能调用剪切板的解决方法
- C#线程同步的三类情景分析
- C#子线程更新UI控件的方法实例总结
- C++使用CriticalSection实现线程同步实例
- 基于C++实现的线程休眠代码
- VB读取线程、句柄及写入内存的API代码实例
- C#网络编程基础之进程和线程详解
- C#多线程处理多个队列数据的方法
- C#实现线程安全的简易日志记录方法
- C#中线程同步对象的方法分析
- ASP.NET线程相关配置
- 浅析linux环境下一个进程最多能有多少个线程
- 再谈JavaScript线程
- C#实现终止正在执行的线程
- 解析Java线程同步锁的选择方法
- 深入Android线程的相关问题解惑
- 深入探讨linux下进程的最大线程数、进程最大数、进程打开的文件数
- Java线程关闭的3种方法
- JAVA实现线程的三种方法
- 深入Java线程中断的本质与编程原则的概述