——java多线程机制

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

进程:

   是一个正在执行中的程序。

   没一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者就一个控制单元。

   一个进程中至少有一个线程。

线程:

   就是进程中的一个独立的控制单元。线程在控制着进程的执行。



java JVM虚拟机启动时会有一个java.exe


   该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在与main方法中。

   该线程称为--主线程,名称为:main。

    扩展:

    其实java 虚拟机JVM启动时是两个线程:一个是主线程,另一个垃圾回收机制的线程。


多线程的运行状态:

1,创建:线程被创建;

实例代码:

Thread thread = new Thread();

2,运行:通过start方法运行线程,有运行资格和运行权。

实例代码:

thread.start();

3,冻结:如果线程被sleep,或者wait,则进入冻结状态。放弃了运行权。

实例代码:

thread.sleep(12);或者是

thread.wait();

4,阻塞(临时)状态:线程只有执行资格,没有执行权。当线程被唤醒

         或者sleep时间到的时候,可能会进入阻塞状态,获得执行资格,但没有执行权。

5,消亡:线程调用了stop方法,或者run方法执行完毕之后,线程结束。

注:stop方法已经过时,JDK1.5之后采用了新的停止机制!

多线程获取对象及名称:

static Thread currentThread()获得当前线程的对象。

String getName()获得线程的方法:

默认是:Thread + 编号,编号从0开始。

void setName()设置线程的名称。


线程的定义和启动

创建线程的第一种方式:

     继承Thread类。继承方式

步骤:

1,定义类继承Thread。

2,重写Thread类的run方法。

目的:将自定义代码存储到run方法中,让线程运行。

3,调用线程的start方法,该方法两个作用:启动线程,调用run方法。

   语法:

class 类名 extends Thread{

@Override

public void run(){

//线程执行的代码

}

}

创建线程的第二种方式:实现方式

步骤:

1,定义类实现Runnable接口;

2,重写Runnable接口中的run方法;

  作用:将线程要运行的代码存放到run方法中。

3,通过Thread类建立线程对象;

4,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。

  重写run方法的目的:

因为:自定义的run方法所属的对象是Runnable接口的子类对象

所以要让线程去执行指定对象的run方法,就必须明确该run方法所属的对象。

5,调用Thread类的start方法开启线程,并调用Runnable接口接口子类的run方法。

   语法:

class 类名 implements Runnable{

@Override

public void run(){

//线程执行的代码

}

}   

   实现方式和继承方式有什么区别?

实现方式,避免了java中单继承的局限性。

在定义线程时,建议使用实现方式。

   两种方式的区别:

继承Thread类:线程代码存放在Thread类的run方法中,具有单重继承的局限性

实现Runnable,线程代码存放在Runnable接口的实现类run方法中,没有局限性,更加灵活


  执行机制:

    在多个线程启动后,每一次的运行结果都不同。因为对个线程都在获取CPU的执行权,

    CPU执行到谁,谁就执行。需要明确的是:CPU在某一时刻,只能有一个程序在执行(多核除外)

    CPU在做着快速切换,以达到看上去是同时运行的效果。

    可以形象的把多线程的运行行为看成是在互相抢夺CPU的执行权。

    这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长时间,有CPU决定。

   为什么要重写run方法?

Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码。

该功能就是run方法。即,Thread的类中的run方法,用于存储线程要运行的代码。


   run方法和start方法的区别:

1,run方法时用于存储线程要执行的代码,

  如果直接执行run方法,则此时是主线程在执行run方法。

  此时是单线程,会先执行完run方法中的代码,才去执行其他的代码。

2,start方法是用于启动线程,并执行run方法。此时run方法就由start方法开启的线程执行。

  此时就是主线程和start方法启动的线程在轮流执行。是多线程。

  当线程启动之后,就不能再用同一线程重新启动, 会报IllegalThreadStateException非法线程状态异常。

自定义线程的示例代码:

package com.itheima.threaddemos;

/**
 * 自定义线程类
 * @author wuyong
 *
 */
public class MyThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		//创建用方式一自定义线程
		MyThread mt1 = new MyThread();
		//创建方式二定义的线程
		Thread mt2 = new Thread(new MyThread2());
		
		//调用start方法,来启动线程,和调用run方法
		mt1.start();
		mt2.start();
		
		for (int i = 0; i < 30; i++) {
			System.out.println("系统线程 --" + i + "-- runnig ");
		}	
	}

}
/**
 * 自定义线程类
 * 实现方式:继承Thread类
 * @author wuyong
 *
 */
class MyThread extends Thread{
	/**
	 * 重写Thread类的run方法
	 */
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("继承Thread类的自定义线程1 --" + i + "-- runnig ");
		}
	}
}
/**
 * 自定义线程类
 * 实现方式:实现Runnable接口。
 * @author wuyong
 *
 */
class MyThread2 implements Runnable{
	/**
	 * 重写Thread类的run方法
	 */
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("实现Runnable接口的自定义线程2 --" + i + "-- runnig ");
		}
	}
}


线程的等待和唤醒:

wait(),notify(),notifyAll()用来操作线程为什么定义在Object类中

  1,这些方法存在于同步中。

2,使用这些方法时必须要标识所属的同步锁。

3,锁可以是任意对象,所有任意对象调用的方法一定是定义在Object中。

   wait(),sleep()有什么区别?

wait():释放资源,释放锁。

sleep():释放资源,不释放锁。

wait();//使线程等待

notify();//唤醒线程,通常是唤醒线程池中的第一个被等待的线程。使用notify方法容易只唤醒本方线程,而导致所有线程都等待。

notifyAll();//唤醒线程池中的所有线程。

这些方法一般都使用在同步中,因为要对持有监视器(锁)的线程操作。

所以都要使用在同步中,因为只有同步才具有锁。



为什么这些操作线程的方法都要定义在Object类汇总呢?

因为这些方法在操作同步线程时,都必须要标识他们所操作线程持有的锁,

只有同一个锁上的被等待线程,可以被同一个锁notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。

       而锁可以是任意对象,所以任意对象调用的方法定义在Object类中。

注:当有多个线程同时操作资源时,会导致程序出现错误。因为没有判断标记。

要用while循环判断标记,和使用notifyAll方法唤醒等待中的线程来判断标记。

等待唤醒的示例代码:  

package com.itheima.threaddemos.ThreadCommunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程间通信测试,及其wait,notify,notifyAll方法的测试类
 * @author wuyong
 *
 */
public class ThreadCommunicationTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Resorce2 res2 = Resorce2.getInstance();
		
		InputThread2 in = new InputThread2(res2);
		OutputThread2 out = new OutputThread2(res2);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(in);
		Thread t3 = new Thread(out);
		Thread t4 = new Thread(out);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
/**
 * 资源类
 * @author wuyong
 *
 */
class Resorce2{
	private static final Resorce2 res2 = new Resorce2();
	private Resorce2(){};
	public static Resorce2 getInstance(){return res2;}
	//姓名
	private String name;
	//国家
	private String country;
	//状态标示,用于控制线程是否等待或者唤醒
	private boolean flag;
	
	private Lock lock = new ReentrantLock();
	private Condition con_pro = lock.newCondition();
	private Condition con_cus = lock.newCondition();
	/**
	 * 设置资源类的属性,并切换线程的等待或者唤醒,同步方法
	 * @param name
	 * @param country
	 */
	public synchronized void set(String name,String country) {
//		if (flag) {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		this.country = country;
		System.out.println("现在出场的球星是---" + this.name);
		flag = true;
//		this.notify();
		this.notifyAll();
	}
	public void set2(String name,String country) {
		lock.lock();
		try {
			while (flag)
				con_pro.await();
			this.name = name;
			this.country = country;
			System.out.println("现在出场的球星是---" + this.name);
			flag = true;
			con_cus.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 打印出资源的信息,同步方法
	 */
	public synchronized void out() {
		while (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(this.name + "---" + this.country);
		flag = false;
		this.notifyAll();
	}
	public void out2() {
		lock.lock();
		try {
			while (!flag)
				con_cus.await();
			System.out.println(this.name + "---" + this.country);
			flag = false;
			con_pro.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
}
/**
 * 设置资源信息线程类
 * @author wuyong
 *
 */
class InputThread2 implements Runnable{
	private Resorce2 res2;
	public InputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		int i = 0;
		while (true) {
			if (i == 0) 
				res2.set2("kobe", "Amarican");
			else
				res2.set2("姚明", "中国");
			i = (i + 1) % 2;
		}
	}
}

/**
 * 输出资源信息线程类
 * @author wuyong
 *
 */
class OutputThread2 implements Runnable{
	private Resorce2 res2;
	public OutputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		while (true) {
			res2.out2();
		}
	}
}

在JDK1.5中,新的等待唤醒机制:

  将synchronized替换成了Lock接口操作。

  将Object中的wait,notify,notifyAll,替换成了Condition对象,

  该对象可以lock所,进行获取。

  注:一个Lock对象可以有多个Condition实例,可以用对应的Condition实例,操作对应的线程。

  其实:相当于就是用Lock代替了synchronized的操作,而Condition写的与是代替了Object中的wait,notify,notifyAll。

  示例代码:

package com.itheima.threaddemos;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * JDK1.5之后的新的等待和唤醒机制
 * @author wuyong
 *
 */
public class Resource
{
    private String name ;
    private int count = 1 ;
    private boolean flag = false ;
    
    //线程锁
    private Lock lock = new ReentrantLock();
    //Condition实例对象,控制线程的等待和唤醒
    private Condition condition_pro = lock. newCondition();
    private Condition condition_con = lock. newCondition();


    /**
     * 生产
     * @param name
     * @throws InterruptedException
     */
    public  void produce( String name)throws InterruptedException
    {
        lock .lock();
         try
         {
             while(flag )
                condition_pro .await(); //生产线程等待
             this.name = name+ "--"+count ++;


            System .out. println(Thread .currentThread().getName()+"...生产者.." +this.name);
            flag = true ;
            condition_con .signal();//消费线程唤醒
            condition_con .signalAll();//所有消费线程唤醒
            
         }
         finally
         {
            lock .unlock(); //释放锁的动作一定要执行。
         }
    }
    
    /**
     * 消费
     * @throws InterruptedException
     */
    public  void consume() throws InterruptedException
    {
        lock .lock();//获取锁
         try
         {
             while(!flag )
                condition_con .await();//消费线程等待
            System .out. println(Thread .currentThread().getName()+"...消费者........." +this.name);
            flag = false ;
            condition_pro .signal();//生产线程唤醒
            condition_pro .signalAll();//所有生产线程唤醒
         }
         finally
         {
            lock .unlock();//释放锁
         }
    }
}



线程的停止:

由于Thread类中的stop方法已经过时,stop方法偶安全问题,所有不建议使用stop方法来停止。

    如何停止线程呢?

    只有一种方法,就是让run方法结束,

    开启多线程是,运行代码通常使用循环结构,所以控制了循环,就可以使run方法结束,即线程结束。

   
    特殊情况:

        当线程处于了冻结状态:如:wait,sleep,join。

        就不会读取到标记。那么线程就不会结束。


   解决方案:强制清除线程的冻结状态,获得执行资格。     

    当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结进行清除。

   强制让线程恢复到运行状态中来,这样就可以操作标记,让线程结束。

    而Thread类提供了该方法 interrupt();

    interrupt()方法时强制将处于冻结状态下的线程恢复到运行状态。

结束线程的实例代码:

package com.itheima.threaddemos.ThreadCommunication;

/**
 * 线程停止测试类
 * 条件:必须是多线程下,的同步代码。
 * 由于Thread类中的stop方法已经过时,
 * 如何停止线程呢?
 * 只有一种方法,就是让run方法结束,
 * 开启多线程是,运行代码通常使用循环结构,所以控制了循环,就可以使run方法结束,即线程结束。
 * 
 * 特殊情况:
 *   当线程处于了冻结状态:如:wait,sleep,join。
 *   就不会读取到标记。那么线程就不会结束。
 *   
 * 当没有指定的方式让冻结的线程恢复到运行状态时,这时就需要对冻结进行清除。
 * 强制让线程恢复到运行状态中来,这样就可以操作标记,让线程结束。
 * 而Thread类提供了该方法 interrupt();
 * @author wuyong
 *
 */
public class ThreadStopTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		MyStopThread mst = new MyStopThread();
		
		Thread t1= new Thread(mst);
		Thread t2 = new Thread(mst);
		
		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();
		int i = 0;
		while(true){
			if (i == 60) { 
//				强制将冻结状态下的线程恢复到运行状态,使线程对标记进行判断,从而到达控制循环,以结束run方法。
				t1.interrupt();
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName() + "---run");
			i++;
		}
	}

}
/**
 * 自定义线程类
 * @author wuyong
 *
 */
class MyStopThread implements Runnable{
	//控制线程状态的标记
	boolean flag = true;
	@Override
	public synchronized void run() {
		while (flag) {
			try {
				this.wait();//线程等待(冻结状态)
			} catch (InterruptedException e) {
				flag = false;
				System.out.println(Thread.currentThread().getName() + "stop");
			}
			System.out.println(Thread.currentThread().getName() + "...run");
		}
	}
	
}


多线程的安全问题。

   问题原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,

另一个线程参与进来执行。导致共享数据的错误。

   解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中其他线程不能参与执行。

   java对于线程安全问题提供了专业的解决方式。

    方式一:同步代码块

   synchronized(对象)//这称为锁(同步锁)。必须要是同一对象。持有锁的线程才可以在同步中执行,

      //没有锁的线程即使获得CPU执行权也无法执行。例如火车的卫生间。 

    {

    //需要被同步的代码

  }

    方式二:同步方法。

    访问修饰符 sychronized 返回值类型 方法名(参数列表){

//方法体

    }

   注:同步函数中,使用的锁是this。即函数所属对象的引用。

而静态的同步方法,使用的锁是该方法所属在类的字节码文件对象。

因为:

  静态进入内存时,内存中还没有本类对象,但是一定有该类对应的字节码文件对象。

  类名.class 该对象的类型是Class。

    静态同步方法的语法格式:

    访问修饰符 static sychronized 返回值类型 方法名(方法所属类名.class){

//方法体

    }

   如何确定同步那些地方:

       1,明确那些代码是对线程运行代码。

       2,明确那些是共享数据。(一般是成员变量)

       3,明确多线程运行代码中哪些语句是操作共享数据的。

   同步的前提:

1,要有多个线程。

2,多个线程共同使用一个所,就是共享同一资源。

3,同步中只能有一个线程在执行。

   同步的好处:解决了多线程的安全问题。

    弊端:多个线程执行时,每次都要对所进行判断,较为消耗资源。

多线程同步的示例代码:

package com.itheima.threaddemos.synchronizeds;

/**
 * 同步代码块测试
 * @author wuyong
 *
 */
public class SynchronizedTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建自定义线程类对象
		MyThread mt = new MyThread();
		//创建四个线程
		Thread t = new Thread(mt);
		Thread t1 = new Thread(mt);
		Thread t2 = new Thread(mt);
		Thread t3 = new Thread(mt);
		//启动四个线程
		t.start();
		t1.start();
		t2.start();
		t3.start();
	}

}
/**
 * 自定义线程类,实现方式为,实现Runnable接口
 * @author wuyong
 *
 */
class MyThread implements Runnable{
	//票数
	private int tickets = 400;
	Object obj = new Object();
	/**
	 * 实现Runnable中的run方法,将线程执行的代码保存至run方法中
	 */
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			//同步代码块
			synchronized (obj) {//此处的对象参数 必须保证是同一个,否则同步失败
				if (tickets > 0) {
					//让线程休眠,此时如果不同步就会出现线程安全问题,
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					//让票数递减
					System.out.println(Thread.currentThread().getName()
							+ ":ticket//" + tickets--);
				} else break; //退出循环
			}
		}
	}
	
}



多线程的综合示例:

生产者和消费者的模拟

package com.itheima.threaddemos;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 生产者和消费者的模拟
 * 思路:
 * 	1,提供一个商品类,其中定义一个商品生产的状态标标识;
 * 	2,自定义一个生产者的线程类;
 * 	3,自定义一个消费者的线程类;
 * 	4,在自定义线程类的同步代码中,循环判断商品的生产状态。
 * @author wuyong
 *
 */
public class Pro_CusDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//创建一个商品对象
		Goods goods = new Goods();
		
		//创建生产线程
		Pro_Thread pro = new Pro_Thread(goods);
		//创建消费线程
		Cus_Thread cus = new Cus_Thread(goods);
		
		//创建4个线程
		Thread tp1 = new Thread(pro);
		Thread tp2 = new Thread(pro);
		Thread tc1 = new Thread(cus);
		Thread tc2 = new Thread(cus);
		
		//开启线程,开始生产和消费
		tp1.start();
		tp2.start();
		tc1.start();
		tc2.start();
	}

}

/**
 * 商品类
 * @author wuyong
 *
 */
class Goods{
	//商品名称
	private String name;
	//商品数量
	private int counts;
	//商品生产状态
	private boolean flag;
	
	//JDK1.5后的锁对象
	Lock lock = new ReentrantLock();
	
	//生产者的Condition对象
	Condition pro_con = lock.newCondition();
	//消费者的Condition对象
	Condition cus_con = lock.newCondition();
	
	/**
	 * 生产的方法
	 * JDK1.5之前的方式,用同步来实现
	 * @param name
	 */
	public synchronized void produce(String name){
			while (flag) {//循环判断标识
				try {
					wait();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			counts++;
			this.name = name + counts;
			System.out.println(Thread.currentThread().getName() + "生产了++++++" + this.name);
			flag = true;
			notifyAll();
	}
	/**
	 * 消费的方法
	 * JDK1.5之前的方式,用同步来实现
	 * @param name
	 */
	public synchronized void customer(){
			while (!flag) {//必须要循环判断标识!!!!
				try {
					wait();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName() + "消费了" + name);
			flag = false;
			notifyAll();
	}
	/**
	 *  生产的方法
	 *	JDK1.5 中提供的多线程升级解决方案。
	 *	将同步Synchronized替换成现实Lock操作。
	 *	将Object中的wait,notify notifyAll,替换了Condition对象。
	 *	该对象可以Lock锁 进行获取。
	 *	该示例中,实现了本方只唤醒对方操作。

	 *	Lock:替代了Synchronized
     *	lock  //获得锁
     *	unlock //释放锁,这一步必须要执行
     *	newCondition()  //获得Condition对象
     *
	 *	Condition:替代了Object wait notify notifyAll
     *	await();  //线程等待
     *	signal();	//唤醒单个线程
     *	signalAll();//唤醒所有线程
     *
	 * @param name
	 */
	public void produce2(String name){
		try {
			lock.lock();
			while (flag) {
				try {
					pro_con.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			counts++;
			this.name = name + counts;
			System.out.println(Thread.currentThread().getName() + "生产了++++++" + this.name);
			flag = true;
			cus_con.signal();
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 消费的方法
	 * JDK1.5 中提供的多线程升级解决方案。
	 */
	public void customer2(){
		try {
			lock.lock();
			while (!flag) {//必须要循环判断标识!!!!
				try {
					cus_con.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName() + "消费了" + name);
			flag = false;
			pro_con.signal();
		} finally{
			lock.unlock();
		}
}
}
/**
 * 生产者
 * @author wuyong
 *
 */
class Pro_Thread implements Runnable{
	private Goods goods;
	
	public Pro_Thread(Goods goods){ 
		this.goods = goods;
	}

	@Override
	public void run() {
		while (true) {
			//JDK1.5之前的等待唤醒机制
			goods.produce("金克拉");
			//JDK1.5之后的等待唤醒机制
//			goods.produce2("金克拉");
		}
	}
	
}
/**
 * 消费者
 * @author wuyong
 *
 */
class Cus_Thread implements Runnable{
	
	private Goods goods;

	public Cus_Thread(Goods goods){ 
		this.goods = goods;
	}
	@Override
	public void run() {
		while (true) {
			//JDK1.5之前的等待唤醒机制
			goods.customer();
			//JDK1.5之后的等待唤醒机制
//			goods.customer2();
		}
	}	
}

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

wait(),notify(),notifyAll()用来操作线程为什么定义在Object类中

  1,这些方法存在于同步中。

2,使用这些方法时必须要标识所属的同步锁。

3,锁可以是任意对象,所有任意对象调用的方法一定是定义在Object中。

   wait(),sleep()有什么区别?

wait():释放资源,释放锁。

sleep():释放资源,不释放锁。

wait();//使线程等待

notify();//唤醒线程,通常是唤醒线程池中的第一个被等待的线程。使用notify方法容易只唤醒本方线程,而导致所有线程都等待。

notifyAll();//唤醒线程池中的所有线程。

等待唤醒的示例代码:  

package com.itheima.threaddemos.ThreadCommunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程间通信测试,及其wait,notify,notifyAll方法的测试类
 * @author wuyong
 *
 */
public class ThreadCommunicationTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Resorce2 res2 = Resorce2.getInstance();
		
		InputThread2 in = new InputThread2(res2);
		OutputThread2 out = new OutputThread2(res2);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(in);
		Thread t3 = new Thread(out);
		Thread t4 = new Thread(out);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
/**
 * 资源类
 * @author wuyong
 *
 */
class Resorce2{
	private static final Resorce2 res2 = new Resorce2();
	private Resorce2(){};
	public static Resorce2 getInstance(){return res2;}
	//姓名
	private String name;
	//国家
	private String country;
	//状态标示,用于控制线程是否等待或者唤醒
	private boolean flag;
	
	private Lock lock = new ReentrantLock();
	private Condition con_pro = lock.newCondition();
	private Condition con_cus = lock.newCondition();
	/**
	 * 设置资源类的属性,并切换线程的等待或者唤醒,同步方法
	 * @param name
	 * @param country
	 */
	public synchronized void set(String name,String country) {
//		if (flag) {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		this.country = country;
		System.out.println("现在出场的球星是---" + this.name);
		flag = true;
//		this.notify();
		this.notifyAll();
	}
	public void set2(String name,String country) {
		lock.lock();
		try {
			while (flag)
				con_pro.await();
			this.name = name;
			this.country = country;
			System.out.println("现在出场的球星是---" + this.name);
			flag = true;
			con_cus.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 打印出资源的信息,同步方法
	 */
	public synchronized void out() {
		while (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(this.name + "---" + this.country);
		flag = false;
		this.notifyAll();
	}
	public void out2() {
		lock.lock();
		try {
			while (!flag)
				con_cus.await();
			System.out.println(this.name + "---" + this.country);
			flag = false;
			con_pro.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
}
/**
 * 设置资源信息线程类
 * @author wuyong
 *
 */
class InputThread2 implements Runnable{
	private Resorce2 res2;
	public InputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		int i = 0;
		while (true) {
			if (i == 0) 
				res2.set2("kobe", "Amarican");
			else
				res2.set2("姚明", "中国");
			i = (i + 1) % 2;
		}
	}
}

/**
 * 输出资源信息线程类
 * @author wuyong
 *
 */
class OutputThread2 implements Runnable{
	private Resorce2 res2;
	public OutputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		while (true) {
			res2.out2();
		}
	}
}


这些方法一般都使用在同步中,因为要对持有监视器(锁)的线程操作。

所以都要使用在同步中,因为只有同步才具有锁。



为什么这些操作线程的方法都要定义在Object类汇总呢?

因为这些方法在操作同步线程时,都必须要标识他们所操作线程持有的锁,

只有同一个锁上的被等待线程,可以被同一个锁notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。

       而锁可以是任意对象,所以任意对象调用的方法定义在Object类中。

注:当有多个线程同时操作资源时,会导致程序出现错误。因为没有判断标记。

要用while循环判断标记,和使用notifyAll方法唤醒等待中的线程来判断标记。

等待唤醒的示例代码:  

package com.itheima.threaddemos.ThreadCommunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程间通信测试,及其wait,notify,notifyAll方法的测试类
 * @author wuyong
 *
 */
public class ThreadCommunicationTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Resorce2 res2 = Resorce2.getInstance();
		
		InputThread2 in = new InputThread2(res2);
		OutputThread2 out = new OutputThread2(res2);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(in);
		Thread t3 = new Thread(out);
		Thread t4 = new Thread(out);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
/**
 * 资源类
 * @author wuyong
 *
 */
class Resorce2{
	private static final Resorce2 res2 = new Resorce2();
	private Resorce2(){};
	public static Resorce2 getInstance(){return res2;}
	//姓名
	private String name;
	//国家
	private String country;
	//状态标示,用于控制线程是否等待或者唤醒
	private boolean flag;
	
	private Lock lock = new ReentrantLock();
	private Condition con_pro = lock.newCondition();
	private Condition con_cus = lock.newCondition();
	/**
	 * 设置资源类的属性,并切换线程的等待或者唤醒,同步方法
	 * @param name
	 * @param country
	 */
	public synchronized void set(String name,String country) {
//		if (flag) {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		this.country = country;
		System.out.println("现在出场的球星是---" + this.name);
		flag = true;
//		this.notify();
		this.notifyAll();
	}
	public void set2(String name,String country) {
		lock.lock();
		try {
			while (flag)
				con_pro.await();
			this.name = name;
			this.country = country;
			System.out.println("现在出场的球星是---" + this.name);
			flag = true;
			con_cus.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 打印出资源的信息,同步方法
	 */
	public synchronized void out() {
		while (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(this.name + "---" + this.country);
		flag = false;
		this.notifyAll();
	}
	public void out2() {
		lock.lock();
		try {
			while (!flag)
				con_cus.await();
			System.out.println(this.name + "---" + this.country);
			flag = false;
			con_pro.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
}
/**
 * 设置资源信息线程类
 * @author wuyong
 *
 */
class InputThread2 implements Runnable{
	private Resorce2 res2;
	public InputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		int i = 0;
		while (true) {
			if (i == 0) 
				res2.set2("kobe", "Amarican");
			else
				res2.set2("姚明", "中国");
			i = (i + 1) % 2;
		}
	}
}

/**
 * 输出资源信息线程类
 * @author wuyong
 *
 */
class OutputThread2 implements Runnable{
	private Resorce2 res2;
	public OutputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		while (true) {
			res2.out2();
		}
	}
}

发布了25 篇原创文章 · 获赞 4 · 访问量 1万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章