线程间通信与线程生命的控制
转载请表明出处:http://blog.youkuaiyun.com/u012637501(嵌入式_小J的天空)
一、线程通信方法
Java是通过Object类的wait、notify、notifyAll这几个方法来实现进程键的通信。由于所有的类都是从Object继承的,因此在任何类中都可以直接使用这些方法。
wait
:告诉当前线程放弃监视器并进入睡眠状态,知道其他线程进入同一监视器并调用notify为止;
notify
:唤醒同一对象监视器中调用wait的第一个线程。用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况;
notifyAll
:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行,用于类似某个不定期的培训班终于招生满额后,通知所有学员都来上课的情况。
注意:这里所谓的同一对象监视器,是指线程间通信的synchronized(object)
中的object对象为同一个对象。
1.简介
生产者Producer.java:当数据库数据被消费者读取后,向一个数据库写入数据;
消费者Customer.java:当数据库有数据且生产者写入完成时,读取里面的数据;
数据库Data.java:一个类,提供读取和写入两种方法,并且为线程同步和同一对象监视器
主线程ThreadCommunication.java:创建两个线程,线程共享同一资源Data.
2.功能源码
实现:Producer线程存放依次数据,Consumer线程就去一次,反之,Producer也必须等到Consumer取完后才能存放新的数据。
(1)Data.java
public class Data
{
String name = "林俊杰"; //初始化数据
String sex="男";
boolean isNull=false; //标识
public synchronized void put(String name,String sex) //存入数据方法
{
if(isNull) //当数据类不为空时获得该旗标的线程进入等待状态
try {wait();} catch (InterruptedException e1) {e1.printStackTrace();}
this.name=name;
try{Thread.sleep(100);}catch(Exception e){e.printStackTrace();}
this.sex=sex;
isNull=true;
notify();
}
public synchronized void get() //读取数据方法
{
if(!isNull)//当数据还没有被写入完成时获取该旗标的线程进入等待状态
try {wait();} catch (InterruptedException e1) {e1.printStackTrace();}
System.out.println(name+" : "+sex);
isNull=false;
notify();
}
}
(2)Producer.java
public class Producer implements Runnable
{
Data data=null;
public Producer(Data data) //构造方法
{
this.data=data;
}
public void run() {
int i=0;
while(true)
{
if(i==0)
{
data.put("吴倩","女");
}
else
{
data.put("林俊杰","男");
}
i=(i+1)%2;
}
}
}
(3)Customer.java
//消费者类
class Customer implements Runnable
{
Data data=null;
public Customer(Data data) {
this.data=data;
}
public void run()
{
while(true)
{
data.get();
}
}
}
(4)ThreadCommunication.java
//主类
public class ThreadCommunication
{
public static void main(String[] args)
{
Data data = new Data();
new Thread(new Producer(data)).start(); //生产者线程
new Thread(new Customer(data)).start();//消费者线程
}
}
效果演示:
总结:wait、notify、notifyAll这三个方法只能在synchronized方法中调用,即无论线程调用一个对象的wait还是notify方法,该线程必须先得到该对象的锁旗标。这样,notify只能唤醒同一对象监视器中调用wait的线程,使用多个对象监视器,我们就可以分组有多个wait、notify的情况,同组里的wait只能被同组的notify唤醒。
三、线程生命的控制
1.线程的运行
一个线程的产生是从我们调用了start方法开始进入Runnable状态,即可以被调度运行状态,并没有真正开始运行,调度器可以将CPU分配给它,真正运行其中的程序代码。线程在运行过程中,有以下几个可能的去向:
(1)没有遇到任何阻隔,运行完成直到结束,也就是run()方法执行完毕;
(2)调度器将CPU分配给其他线程,这个线程又变为Runnable状态;
(3)请求锁旗标,却得不到,这时候它要等待对象的锁旗标,得到锁旗标后会进入Runnable状态开始运行;
(4)遇到wait方法,他会被放入等待池中继续等待,直到有notify()或interrupt()方法执行,它才会别唤醒或打断开始等待对象锁旗标,等到锁旗标后进入Runnable状态继续执行。
2.控制线程生命
控制线程生命周期的方法有很多种,如:suspend方法,resume方法和stop方法。但是,一般不推荐使用这三种方法,其中,不推荐使用suspend和resume是因为:
(1)会导致死锁的发生;
(2)它允许一个线程通过直接控制另外一个线程的代码来直接控制哪个线程。
另外,虽然stop能够避免死锁的发生,但是带来了另外的不足,如果一个线程正在操作共享数据段,操作过程没有完成就stop了的话,将会导致数据的不完整性。
下面我们介绍一种方法---设置标识,来控制线程的生命周期:
threadRunnable.java
public class threadRunnable implements Runnable
{
boolean stopFlag=true; //定义一个标识
//停止线程方法
public void stopMe()
{
stopFlag=false;
}
//run方法
public void run()
{
while(stopFlag)
{
System.out.println(Thread.currentThread().getName()+" is running");
}
}
}
ThreadLife.java
public class ThreadLife
{
public static void main(String[] args)
{
threadRunnable runnable = new threadRunnable();
new Thread(runnable).start();
for(int i=0;i<100;i++)
{
if(i==50) //当i累加到50时,结束子线程
{
runnable.stopMe();
}
System.out.println("MainThread is running");
}
}
}
效果演示:当i累加到50时,子线程结束。