public class ThreadCommunation {
public static void main(String[] args) {
Q q = new Q();
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
class Q{
String name="陈琼";
String sex="女";
}
class Producer implements Runnable{
Q q=null;
public Producer(Q q){
this.q=q;
}
public void run() {
int i=0;
while (true){
if (i==0){
q.name="张孝祥";
try {
Thread.sleep(10);
}catch (Exception e){
System.out.println(e.getMessage());
}
q.sex="男";
}else {
q.name="陈琼";
q.sex="女";
}
i=(i+1)%2;
}
}
}
class Consumer implements Runnable{
Q q=null;
public Consumer(Q q){
this.q=q;
}
public void run() {
while (true){
System.out.println(q.name+"---->"+q.sex);
}
}
}
public class ThreadCommunation1 {
public static void main(String[] args) {
QQ q = new QQ();
new Thread(new Producer1(q)).start();
new Thread(new Consumer1(q)).start();
}
}
class QQ{
String name="陈琼";
String sex="女";
}
class Producer1 implements Runnable{
QQ q=null;
public Producer1(QQ q){
this.q=q;
}
public void run() {
int i=0;
while (true){
synchronized (q) {
if (i == 0) {
q.name = "张孝祥";
try {
Thread.sleep(10);
} catch (Exception e) {
System.out.println(e.getMessage());
}
q.sex = "男";
} else {
q.name = "陈琼";
q.sex = "女";
}
}
i=(i+1)%2;
}
}
}
class Consumer1 implements Runnable{
QQ q=null;
public Consumer1(QQ q){
this.q=q;
}
public void run() {
while (true){
synchronized (q){
System.out.println(q.name+"---->"+q.sex);
}
}
}
}
public class ThreadCommunation1 {
public static void main(String[] args) {
QQ q = new QQ();
new Thread(new Producer1(q)).start();
new Thread(new Consumer1(q)).start();
}
}
class QQ{
private String name="陈琼";
private String sex="女";
public synchronized void put(String name,String sex){
this.name=name;
try {
Thread.sleep(10);
}catch (Exception e){
System.out.println(e.getMessage());
}
this.sex=sex;
}
public synchronized void get(){
System.out.println(name+"--->"+sex);
}
}
class Producer1 implements Runnable{
QQ q=null;
public Producer1(QQ q){
this.q=q;
}
public void run() {
int i=0;
while (true){
if (i==0)
q.put("张孝祥","男");
else
q.put("陈琼","女");
i=(i+1)%2;
}
}
}
class Consumer1 implements Runnable{
QQ q=null;
public Consumer1(QQ q){
this.q=q;
}
public void run() {
while (true){
q.get();
}
}
}
我们通过这样的一个应用来讲解线程间的通信。有一个数据存储空间,划分为两部分部分用于存储人的姓名,另一部分用于存储人的性别。我们的应用包含两个线程,一个线程向数据存储空间添加数据(生产者),另一个线程从数据存储空间中取出数据(消费者)
第一个意外,假设生产者线程刚向数据存储空间中添加了一个人的姓名,还没有加入这个人的性别,CPU就切换到了消费者线程,消费者线程将把这个人的姓名和上一个人的性别联系到了一起
Consumer 线程对 Producer 线程放入的一次数据连续读取了多次,并不符合我们的期望。我们要求的结果是,Producer 放一次数据,Consumer 就取一次,反之,Producer 也必须等到 Consumer 取完后才能放入新的数据,这就是我们要讲到的线程间的通信问题,Java 是通过Object类的 wait、notify、notifyAll这几个方法来实现线程间的通信的,由于所有的类都是从 Obiect 继承的,因此在任何类中都可以直接使用这些方法。
wait: 告诉当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用notify 为止
notify:唤醒同一对象监视器中调用 wait 的第一个线程。用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况。
notifyAll:唤醒同一对象监视器中调用 wait 的所有线程,具有最高优先级的线程首先被唤醒并执行。用于类似某个不定期的培训班终于招生满额后,通知所有学员都来上课的情况。
wait、notify、notifyAl1 这三个方法只能在 synchronized 方法中调用,即无论线程调用个对象的 wait 还是 notify 方法,该线程必须先得到该对象的锁旗标,这样,notify 只能唤醒同一对象监视器中调用 wait 的线程,使用多个对象监视器,我们就可以分组有多个wait、notify 的情况,同组里的 wait 只能被同组的notify 唤醒。
5.4 线程的生命周期
一个线程的产生是从我们调用了 start 方法开始进入 Runnable 状态即可以被调度运行状态,并没有真正开始运行,调度器可以将 CPU分配给它,真正运行其中的程序代码
(1)没有遇到任何阻隔,运行完成直接结束,也就是run0方法执行完毕。
(2)调度器将CPU分配给其他线程,这个线程又变为 Runnable 状态。
(3)请求锁旗标,却得不到,这时候它要等待对象的锁旗标,得到锁旗标后又会进入Runnable状态开始运行。
(4)遇到wait方法,它会被放入等待池中继续等待,直到有notify0或interrupt0方法执行,它才会被唤醒或打断开始等待对象锁旗标,等到锁旗标后进入 Runable 状态继续执行
了解了线程的生命周期,就不难理解如何控制线程生命的办法了吧?其实,控制线程生命周期的方法有很多种,如:suspend 方法、resume 方法和 stop 方法。但我们不推荐使用这三个方法,其中,不推荐使用 suspend 和resume是因为:
(1)会导致死锁的发生
(2)它允许一个线程(甲) 通过直接控制另外一个线程(乙)的代码来直接控制那个线程(乙)
虽然 stop 能够避免死锁的发生,但是带来了另外的不足,如果一个线程正在操作共享数据段,操作过程没有完成就 stop 了的话,将会导致数据的不完整性。因此 stop 方法也被不提倡使用了
public class ThreadLife {
public static void main(String[] args) {
ThreadTest t = new ThreadTest();
new Thread(t).start();
for (int i = 0; i < 100; i++) {
if (i == 50)
t.stopMe();
System.out.println("mainThread is running");
}
}
}
class ThreadTest implements Runnable{
private boolean bFlag=true;
public void stopMe(){
bFlag=false;
}
public void run() {
while (bFlag){
System.out.println(Thread.currentThread().getName()+"is running");
}
}
}
上面的程序中定义了一个计数器i,用来控制 main 线程的循环打印次数,在i的值从0到 50 的这段时间内,两个线程是交替运行的,但当计数器的取值变为 50 的时候,程序调用了ThreadTest 类的 stopMe 方法,而在 stopMe 方法中,将 bFlag 变量赋值为 false,也就是终止了 while 循环,run 方法结束,Thread-1线程随之结束。main 线程在计数器i等于50的时候,调用了ThreadTest 类的 stopMe 方法后,CPU 不一定会马上切换到 Thread-1线程上,也就是说Thread-1 线程不一定会马上终止,main 线程的计数器i可能到达五十几甚至六十几后,Thread-1 线程才真正结束。