黑马程序员--第一阶段4.多线程--第12天
线程间的通信
要求存入一条数据,打出一条数据:
class Producer implements Runnable
{
Q q;
public Producer(Q q)
{
this.q=q;
}
public void run()
{
boolean b=false;
while(true)
{
synchronized(q)
{
b = b == true ? false:true;//此代码实现真和假的切换
if(b)
{
q.name="小红";
q.sex="女";
}
else
{
q.name="大强";
q.sex="男";
}
}
}
}
}
class Consumer implements Runnable
{
Q q;
public Consumer(Q q)
{
this.q=q;
}
public void run()
{
while(true)
{
synchronized(q)
{
System.out.println(q.name+":"+q.sex);
}
}
}
}
class Q
{
String name="unkown";
String sex="unkown";
}
class ThreadCommunication//线程通信类
{
public static void main(String args[])
{
Q q=new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
//如果上面代码不使用同步代码块,则可能打印出这样的数据:“大强,女 ”、“小红,男”
//上面代码虽然使用了同步代码块,但是有时候同一条数据被连续打印了多次,而我们要求是存一条数据,打出一条数据
所有的类都有以下3个方法:
1. wait:告诉当前线程放弃监视器并进入睡眠状态,则其它线程就有可能得到这个监视器,当其他线程进入这个监视器并调用notify方法后这个线程就回到准备状态,即有可能获得监视器。
2. notify:唤醒同一对象监视器中调用wait的第一个线程。用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况。
3. notifyAll:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。用于类似某个不定期的培训班终于招生满额后,通知所有学员都来上课的情况。
注:以上3个方法必须放在同步代码块或同步方法中,否则会报错。
上面的实例修改后为:
class Producer implements Runnable
{
Q q;
public Producer(Q q)
{
this.q=q;
}
public void run()
{
boolean b=false;
while(true)
{
synchronized(q)
{
if(q.bFull)//如果内容为满则wait
try{q.wait();}catch(Exception e){}
b = b == true ? false:true;//此代码实现真和假的切换
if(b)
{
q.name="小红";
q.sex="女";
}
else
{
q.name="大强";
q.sex="男";
}
q.bFull=true;
q.notify();
}
}
}
}
class Consumer implements Runnable
{
Q q;
public Consumer(Q q)
{
this.q=q;
}
public void run()
{
while(true)
{
synchronized(q)
{
if(!q.bFull)//如果内容为空则wait
try{q.wait();}catch(Exception e){}
System.out.println(q.name+":"+q.sex);
q.bFull=false;
q.notify();
}
}
}
}
class Q
{
boolean bFull=false; //初始内容为空
String name="unkown";
String sex="unkown";
}
class ThreadCommunication//线程通信类
{
public static void main(String args[])
{
Q q=new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
以上代码还可以修改如下:(这样写更加整洁直观,没那么容易出错)
class Producer implements Runnable
{
Q q;
public Producer(Q q)
{
this.q=q;
}
public void run()
{
boolean b=false;
while(true)
{
b = b == true ? false:true;
if(b)
q.put("周星驰", "男");
else
q.put("张含韵", "女");
}
}
}
class Consumer implements Runnable
{
Q q;
public Consumer(Q q)
{
this.q=q;
}
public void run()
{
while(true)
{
q.get();
}
}
}
class Q
{
boolean bFull=false;
String name="unkown";
String sex="unkown";
public synchronized void put(Stringname,String sex)
{
if(bFull)
try{wait();}catch(Exception e){}
this.name=name;
this.sex=sex;
bFull=true;
notify();
}
public synchronized void get()
{
if(!bFull)
try{wait();}catch(Exception e){}
System.out.println(name+":"+sex);
bFull=false;
notify();
}
}
class ThreadCommunication
{
public static void main(String args[])
{
Q q=new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
线程生命的控制
线程执行start()后,并没有马上执行run方法里的代码,只是进入Runnable(可运行)状态,然后由Scheduler(调度器)从处于Runnable状态的线程中选择其中一个线程进入Running状态(即执行run方法里的代码),
进入Running状态后有接着有3种可能:
run() completes,run方法里的代码执行完后,这个线程就结束了
Blocked in Object's lock pool:遇到synchronized语句,则这个线程被阻塞在这个对象的锁池里,当得到这个对象的锁旗标时则进入Runnable状态。
遇到wai()方法,则该线程被阻塞在该对象的wait池中,直到调用了这个对象的notify()方法时,进入到这个对象的锁池里。
如何控制一个线程的生命周期:
Thread类中提供了3个方法:suspend(阻塞)、resume(恢复线程的执行)、stop(停止线程),这几个方法都是过时的方法了,因为容易导致死锁问题。
要控制一个线程的生命周期可以用结束线程代码的方法,如把while循环中的条件通过第二个线程设为假.