黑马程序员——线程间通信

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

线程间通信多个线程在操作同一个资源,但是操作的动作却不一样。

 

设计思路

1:将资源封装成对象。

2:将线程执行的任务(任务其实就是run方法。)也封装成对象。

3:两个动作必须要同步资源。

class Res
{
	String name;
	String sex;
	boolean flag = false;
}

class Input implements Runnable
{
	private Res r ;
	Input(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			{
				if(x==0)
				{
					r.name="mike";
					r.sex="man";
				}
				else
				{
					r.name="丽丽";
					r.sex = "女女女女女";
				
class Res
{
	private String name;
	private String sex;
	private boolean flag = false;

	public synchronized void set(String name,String sex)
	{
		if(flag)
			try{this.wait();}catch(Exception e){}
		this.name = name;
		
		this.sex = sex;
		flag = true;
		this.notify();
	}
	public synchronized void out()
	{
		if(!flag)
			try{this.wait();}catch(Exception e){}
		System.out.println(name+"........"+sex);
		flag = false;
		this.notify();
	}
}

}x = (x+1)%2;r.flag = true;r.notify();}}}}class Output implements Runnable{private Res r ;Output(Res r){this.r = r;}public void run(){while(true){synchronized(r){System.out.println(r.name+"...."+r.sex);r.flag = false;r.notify();}}}}class InputOutputDemo{public static void main(String[] args) {Res r = new Res();Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}

 结果: 
  

mike . . . .man

mike . . . .man

mike . . . .man

丽丽 . . . .女女女女女

丽丽 . . . .女女女女女

丽丽 . . . .女女女女女

丽丽 . . . .女女女女女

由运行结果可以发现没有了安全问题,但是程序出现线程霸占、抢占cpu问题,导致程序大量的输出覆盖内,这时java就引用:等待唤醒机制

 

等待唤醒机制:涉及的方法:

wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。

notify唤醒线程池中某一个等待线程。

notifyAll:唤醒的是线程池中的所有线程。

 

注意:

1:这些方法都需要定义在同步中。

2:因为这些方法必须要标示所属的锁。

    你要知道 A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。

3:这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?

    因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。

把代码优化下

class Res
{
	private String name;
	private String sex;
	private boolean flag = false;

	public synchronized void set(String name,String sex)
	{
		if(flag)
			try{this.wait();}catch(Exception e){}
		this.name = name;
		
		this.sex = sex;
		flag = true;
		this.notify();
	}
	public synchronized void out()
	{
		if(!flag)
			try{this.wait();}catch(Exception e){}
		System.out.println(name+"........"+sex);
		flag = false;
		this.notify();
	}
}

运行结果:

mike . . . .man

丽丽 . . . .女女女女女

mike . . . .man

丽丽 . . . .女女女女女


线程的停止:通过stop方法就可以停止线程。但是这个方式过时了。

停止线程:原理就是:让线程运行的代码结束,也就是结束run方法。

怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。

第一种方式:定义循环的结束标记。

第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。

 

---------< java.lang.Thread>----------

interrupt()中断线程。

setPriority(int newPriority)更改线程的优先级。

getPriority()返回线程的优先级。

toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。

Thread.yield()暂停当前正在执行的线程对象,并执行其他线程。

setDaemon(true)将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

join临时加入一个线程的时候可以使用join方法。

当A线程执行到了B线程的join方式。A线程处于冻结状态,释放了执行权,B开始执行。A什么时候执行呢?只有当B线程运行结束后,A才从冻结状态恢复运行状态执行。


====================================================================================================

Lock接口:多线程在JDK1.5版本升级时,推出一个接口Lock接口

解决线程安全问题使用同步的形式,(同步代码块,要么同步函数)其实最终使用的都是锁机制。

 

到了后期版本,直接将锁封装成了对象。线程进入同步就是具备了锁,执行完,离开同步,就是释放了锁。

在后期对锁的分析过程中,发现,获取锁,或者释放锁的动作应该是锁这个事物更清楚。所以将这些动作定义在了锁当中,并把锁定义成对象。

 

所以同步是隐示的锁操作,而Lock对象是显示的锁操作,它的出现就替代了同步。

 

在之前的版本中使用Object类中wait、notify、notifyAll的方式来完成的。那是因为同步中的锁是任意对象,所以操作锁的等待唤醒的方法都定义在Object类中。

 

而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法 await()、signal()、signalAll()体现新版本对象的好处。

< java.util.concurrent.locks >Condition接口:await()、signal()、signalAll();


import java.util.concurrent.locks.*;

class ProducerConsumerDemo 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();

		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);

		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(pro);
		Thread t3 = new Thread(con);
		Thread t4 = new Thread(con);

		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}

class Resource
{
	private String name;
	private int count = 1;
	private boolean flag = false;
			//  t1    t2
	private Lock lock = new ReentrantLock();
	private Condition condition_pro = lock.newCondition();//生产者锁
	private Condition condition_con = lock.newCondition();//消费者锁

	public  void set(String name)throws InterruptedException
	{
		lock.lock();
		try
		{
			while(flag)
				condition_pro.await();//t1,t2
			this.name = name+"--"+count++;

			System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
			flag = true;
			condition_con.signal();//唤醒 消费者
		}
		finally
		{
			lock.unlock();//释放锁的动作一定要执行。
		}
	}

	public  void out()throws InterruptedException
	{
		lock.lock();
		try
		{
			while(!flag)
				condition_con.await();
			System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
			flag = false;
			condition_pro.signal();//唤醒 生产者者
		}
		finally
		{
			lock.unlock();
		}
		
	}
}
class Producer implements Runnable
{
	private Resource res;

	Producer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				res.set("+商品+");
			}
			catch (InterruptedException e)
			{
			}
			
		}
	}
}

class Consumer implements Runnable
{
	private Resource res;

	Consumer(Resource res)
	{
		this.res = res;
	}
	public void run()
	{
		while(true)
		{
			try
			{
				res.out();
			}
			catch (InterruptedException e)
			{
			}
		}
	}
}

运行结果:

Thread-2...消费者.........+商品+--10486
Thread-1...生产者..+商品+--10487
Thread-3...消费者.........+商品+--10487
Thread-0...生产者..+商品+--10488
Thread-2...消费者.........+商品+--10488
Thread-1...生产者..+商品+--10489


你可能感兴趣的:(黑马程序员——线程间通信)