黑马程序员——JAVA基础之多线程的线程间通讯等

------- android培训java培训期待与您交流! ----------

 

线程间通讯:

其实就是多个线程在操作同一个资源,但是动作不同。

wait();

在其他线程调用此对象的notify()方法或notifyAll()方法,或者超过指定的时间量前,导致当前线程等待。

当前线程必须拥有此对象监视器。

notify();

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。

选择是任意性的,并在对实现做出决定时发生。

线程通过调用其中一个 wait 方法,在对象的监视器上等待。 

notifyAll(); 

唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。

 

思考1:wait ( ),notify ( ),notifyAll ( ),用来操作线程为什么定义在了Object 类中?


     1,这些方法存在与同步中。
     2,使用这些方法时必须要标识所属的同步的锁。
     3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

          因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。 
 也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。


思考2:wait ( ) ,sleep ( ) 有什么区别?


     wait ( ) : 释放cpu执行权,释放锁。
     sleep ( ) : 释放cpu执行权,不释放锁。

  

/**
 * 
 * 多线程通讯示例:
 * 	多线程通讯就是多个线程共同操作同一个资源,但是动作不同
 * 例:开启两个线程,交替输入输出两个人的名字和年龄
 *
 */
public class ThreadTest 
{
	public static void main(String[] args)
	{
		Person p = new Person();
		Input in = new Input(p);
		Output out = new Output(p);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		
		t1.start();
		t2.start();
	}
}

//声明一个Person类,有两个属性,姓名和性别
class Person
{
	String name;
	String sex;
}

//声明一个输入线程继承Runnable。传入名字和性别
class Input implements Runnable
{
	private Person p;
	
	Input (Person p)
	{
		this.p = p;
	}
	
	public void run()
	{
		int x = 0;
		
		while(true)
		{
			synchronized (p)
			{
				if (x==0)
				{
					p.name = "lilei";
					p.sex = "boy";
				}
				else 
				{
					p.name = "hanmeimei";
					p.sex = "girl";
				}
				x = (1+x)%2;
			}
		}
	}
}

//声明一个输出线程的类继承Runnable,输出姓名和性别
class Output implements Runnable
{
	private Person p;
	
	Output(Person p)
	{
		this.p = p;
	}
	
	public void run()
	{
		while(true)
		{
			synchronized (p)
			{
				System.out.println("name: "+p.name+"-------sex: "+p.sex);
			}
		}
	}
}


 

等待唤醒机制:

 

改进一下这个程序,让输入输出交替进行


 

/**
 * 
 * 多线程通讯示例:
 * 	多线程通讯就是多个线程共同操作同一个资源,但是动作不同
 * 例:开启两个线程,交替输入输出两个人的名字和年龄
 *
 */
public class ThreadTest 
{
	public static void main(String[] args)
	{
		Person p = new Person();
		Input in = new Input(p);
		Output out = new Output(p);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		
		t1.start();
		t2.start();
	}
}

//声明一个Person类,有两个属性,姓名和性别
class Person
{
	String name;
	String sex;
	boolean flat;
}

//声明一个输入线程继承Runnable。传入名字和性别
class Input implements Runnable
{
	private Person p;
	
	Input (Person p)
	{
		this.p = p;
	}
	
	public void run()
	{
		int x = 0;
		
		while(true)
		{
			synchronized (p)
			{
				if (p.flat)
					try{p.wait();}catch(Exception e){}
				if (x==0)
				{
					p.name = "lilei";
					p.sex = "boy";
				}
				else 
				{
					p.name = "hanmeimei";
					p.sex = "girl";
				}
				x = (1+x)%2;
				p.flat = true;
				p.notify();
			}
		}
	}
}

//声明一个输出线程的类继承Runnable,输出姓名和性别
class Output implements Runnable
{
	private Person p;
	
	Output(Person p)
	{
		this.p = p;
	}
	
	public void run()
	{
		while(true)
		{
			synchronized (p)
			{
				if (!p.flat)
					try{p.wait();}catch(Exception e){}
				System.out.println("name: "+p.name+"-------sex: "+p.sex);
				p.flat = false;
				p.notify();
			}
		}
	}
}


 

发现有些数据不能被外界直接调用,具有安全隐患,所以改进如下:

/**
 * 
 * 多线程通讯示例:
 * 	多线程通讯就是多个线程共同操作同一个资源,但是动作不同
 * 例:开启两个线程,交替输入输出两个人的名字和年龄
 *
 */
public class ThreadTest 
{
	public static void main(String[] args)
	{
		Person p = new Person();
		Input in = new Input(p);
		Output out = new Output(p);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		
		t1.start();
		t2.start();
	}
}

//声明一个Person类,有两个属性,姓名和性别
class Person
{
	//私有数据,更安全
	private String name;
	private String sex;
	private boolean flat;
	
	public synchronized void set(String name,String sex)
	{
		if (flat)
			try{this.wait();}catch(Exception e){}
		this.name = name;
		this.sex = sex;
		flat = true;
		this.notify();	
	}
	
	public synchronized void out()
	{
		if (!flat)
			try{this.wait();}catch(Exception e){}
		System.out.println("name: "+name+"-------sex: "+sex);
		flat = false;
		this.notify();
	}
}

//声明一个输入线程继承Runnable。传入名字和性别
class Input implements Runnable
{
	private Person p;
	
	Input (Person p)
	{
		this.p = p;
	}
	
	public void run()
	{
		int x = 0;
		
		while(true)
		{
			if (x==0)
				p.set("lilei","boy");
			else
				p.set("hanmeimei","girl");
			x = (x+1)%2;			
		}
	}
}

//声明一个输出线程的类继承Runnable,输出姓名和性别
class Output implements Runnable
{
	private Person p;
	
	Output(Person p)
	{
		this.p = p;
	}
	
	public void run()
	{
		while(true)
		{
			p.out();
		}
	}
}



为什么定义notifyAll:

 

因为需要唤醒对方线程。
因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

 

/**
 * 
 * 当出现两个以上多线程进行多线程通讯的时候,需要用的notifyAll();
 * 例子:
 * 		生产者消费者
 *
 */
public class ProCusDemo 
{
	public static void main(String[] args)
	{
		Resource r = new Resource();
		
		Pro p = new Pro(r);
		Cus c = new Cus(r);
		
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p);
		Thread t3 = new Thread(c);
		Thread t4 = new Thread(c);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

//声明一个资源类,赋予基本属性
class Resource
{
	private String name;
	private int count = 1;
	private boolean flat;
	
	public synchronized void set(String name)
	{
		while (flat)//用while让被唤醒的线程再一次判断标记
			try{wait();}catch(Exception e){}
		this.name = name+"-----"+count++;
		
		System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
		flat = true;
		notifyAll();
	}
	
	public synchronized void out()
	{
		while (!flat)//用while让被唤醒的线程再一次判断标记
			try{wait();}catch(Exception e){}
		
		System.out.println(Thread.currentThread().getName()+"---消费者---"+this.name);
		flat = false;
		notifyAll();
	}
}

class Pro implements Runnable
{
	private Resource r;
	
	Pro(Resource r)
	{
		this.r = r;
	}
	
	public void run()
	{
		while (true)
		{
			r.set("商品");
		}
	}
}

class Cus implements Runnable
{
	private Resource r;
	
	Cus(Resource r)
	{
		this.r = r;
	}
	
	public void run()
	{
		while (true)
		{
			r.out();
		}
	}
}


 JDK1.5 中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。

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

该对象可以Lock锁进行获取。

该示例中,实现了本方只唤醒对方操作。
 
Lock:替代了Synchronized 
    lock 
    unlock
    newCondition()
 
Condition:替代了Object wait notify notifyAll
    await();
    signal();
    signalAll();

 

<span style="font-size:14px;">import java.util.concurrent.locks.*;

class ProducerConsumerDemo2 
{
	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;
			
	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)
			{
			}
		}
	}
}
</span>

 

如何停止线程:


     1.  定义循环结束标记,因为线程运行代码一般都是循环,只要控制了循环即可。
     2.  注:stop方法已经过时不再使用。


stop方法已经过时,如何停止线程?


       只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
       特殊情况:当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。


        当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。使用interrupt(中断)方法。
        该方法是结束线程的冻结状态,使线程回到运行状态中来。



 

<span style="font-size:14px;">class StopThread implements Runnable
{
	private boolean flag =true;
	public synchronized void run()
	{
		while(flag)
		{
			try
			{
				wait();
			}
			catch (InterruptedException e)
			{
				System.out.println(Thread.currentThread.getName()+".....Exception");
				flag = false;
			}
			System.out.println(Thread.currentThread.getName()+".......run");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}

class  StopThreadDemo
{
	public static void main(String[] args) 
	{
		StopThread st = new StopThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);

		t1.start();
		t2.start();

		int num = 0;

		while(true)
		{
			if(num++ == 60)
			{
				//st.changeFlag();
				t1.interrupt();
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"......."+num);
		}
		System.out.println("over");
	}
}
</span>

 

守护线程:

前台线程执行完之后后台线程自动执行完,主线程是前台线程。

<span style="font-size:14px;">class StopThread implements Runnable
{
	private boolean flag =true;
	public synchronized void run()
	{
		while(flag)
		{
			try
			{
				wait();
			}
			catch (InterruptedException e)
			{
				System.out.println(Thread.currentThread.getName()+".....Exception");
				flag = false;
			}
			System.out.println(Thread.currentThread.getName()+".......run");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}

class  StopThreadDemo
{
	public static void main(String[] args) 
	{
		StopThread st = new StopThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);

		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();

		int num = 0;

		while(true)
		{
			if(num++ == 60)
			{
				//st.changeFlag();
				//t1.interrupt();
				//t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"......."+num);
		}
		System.out.println("over");
	}
}
</span>

 

 

 

join用法:

当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
 
join可以用来临时加入线程执行。

<span style="font-size:14px;">class Demo implements Runnable
{
	public void run()
	{
		for(int x=0; x<70; x++)
		{
			System.out.println(Thread.currentThread().getName()+"...."+x);
		}
	}
}

class  JoinDemo
{
	public static void main(String[] args) throws Exception
	{
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t1.join();//此时t1线程获得执行权,主线程冻结。
		t2.start();

		for(int x=0; x<80; x++)
		{
			System.out.println("main....."+x);
		}
		System.out.println("over");
	}
}
</span>

 

yield() :

 

   可以稍微减缓程序运行,产生近似于交替运行的效果。

 

class Demo implements Runnable
{
	public void run()
	{
		for(int x=0; x<70; x++)
		{
			System.out.println(Thread.currentThread().toString()+"....."+x);//toString()显示优先级,优先级1~10,默认5
			Thread.yield();//让程序稍微暂停一下,释放执行权,产生交替运行的效果
		}
	}
}

class  JoinDemo
{
	public static void main(String[] args) throws Exception
	{
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		//t1.setPriority(Thread.MAX_PRIORITY); 设置最大优先级
		t2.start();

		for(int x=0; x<80; x++)
		{
			System.out.println("main....."+x);
		}
		System.out.println("over");
	}
}


 

------- android培训java培训、期待与您交流! ----------




原文地址:https://www.cnblogs.com/runwind/p/4212194.html