java多线程(三)线程之间的通信

一个新的线程一旦开启,就几乎不会和其他线程有啥关系了,线程内部是完全独立的。有些时候我们却又希望不同的两个或者多个线程之间能够互相的通信,举个列子。

       有这么一个要求。打印数到控制台,要求子线程打印5次,主线程打印10次,子线程又打印5次,主线程又10次。如此循环100次。这就需要线程之间通信了,子线程打完了,我能不能去通知主线程打,主线程打完了我能不能通知子线程打。这样子不就实现了要求了么?

首先第一点,这两个打印必须是互斥的,不然就全乱套了。所以按照互斥的思路得打如下代码

 

	new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i=1;i<=100;i++){
					synchronized (Object.class) {
						for(int j=1;j<=5;j++){
							System.out.println("这是子线程第"+i+"次循环打印,打印的值为"+j);
						}
						
					}
				}
			}
		}).start();
		
		for(int i=1;i<=100;i++){
			synchronized (Object.class) {
			for(int j=1;j<=10;j++){
				System.out.println("这是主线程第"+i+"次循环打印,打印的值为"+j);
			}
			}
		}

   运行结果

 

 

这是主线程第1次循环打印,打印的值为1
这是主线程第1次循环打印,打印的值为2
这是主线程第1次循环打印,打印的值为3
这是主线程第1次循环打印,打印的值为4
这是主线程第1次循环打印,打印的值为5
这是主线程第1次循环打印,打印的值为6
这是主线程第1次循环打印,打印的值为7
这是主线程第1次循环打印,打印的值为8
这是主线程第1次循环打印,打印的值为9
这是主线程第1次循环打印,打印的值为10
这是子线程第1次循环打印,打印的值为1
这是子线程第1次循环打印,打印的值为2
这是子线程第1次循环打印,打印的值为3
这是子线程第1次循环打印,打印的值为4
这是子线程第1次循环打印,打印的值为5
//这应该打主线程才对呀,顺序给乱了。。。
这是子线程第2次循环打印,打印的值为1
这是子线程第2次循环打印,打印的值为2
这是子线程第2次循环打印,打印的值为3
这是子线程第2次循环打印,打印的值为4

   可以看到我们的手写的代码内部是正确了滴,就是外面的顺序不太多啊,java提供了一套等待唤醒机制来解决该问题。为了方便使用,讲两个打印方法封住进一个资源类中,外部只需要调用就好,不需要关注内部的实现细节。首先是jdk1.5之前的实现方式

//定义一个资源类
//具有两个互斥的方法 outSub outMain
class Resource{
	//定义一个标记表示现在该子线程执行还是main线程执行
	boolean isMain = false;
			
		public synchronized void outSub(int j){
			while(isMain){ //如果是main线程执行呢 就wait自己 使用while双重保险
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			for(int i=1;i<=5;i++){
				System.out.println("这是子线程第"+i+"次循环打印,打印的值为"+j);
			}
			isMain=true;
			//this.notify();//唤醒线程 这里不一定唤醒的是main线程 只是随机唤醒线程池中的一个线程对象 
			//为了保证一定能够唤醒到对方 所以调用notifyAll()唤醒线程池中的所以休眠的线程
			this.notifyAll();
		}
		
		public synchronized void outMain(int j){
			while(!isMain){ //如果是子线程执行就wait 使用while双重保险
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			for(int i=1;i<=10;i++){
				System.out.println("这是主线程第"+i+"次循环打印,打印的值为"+j);
			}
			isMain=false;
			//this.notify();//唤醒线程 这里不一定唤醒的是main线程 只是随机唤醒线程池中的一个线程对象 
			//为了保证一定能够唤醒到对方 所以调用notifyAll()唤醒线程池中的所以休眠的线程
			this.notifyAll();
			
		}
	
}

    在子线程和main线程中只需要调用即可

	public static void main(String[] args) {
		
		final Resource resource = new Resource();
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 1; i <= 50; i++) {
					resource.outSub(i);
				}

			}
		}).start();

		for (int i = 1; i <= 50; i++) {
			resource.outMain(i);
		}

	}

   结果

这是子线程第1次循环打印,打印的值为1
这是子线程第2次循环打印,打印的值为1
这是子线程第3次循环打印,打印的值为1
这是子线程第4次循环打印,打印的值为1
这是子线程第5次循环打印,打印的值为1
这是主线程第1次循环打印,打印的值为1
这是主线程第2次循环打印,打印的值为1
这是主线程第3次循环打印,打印的值为1
这是主线程第4次循环打印,打印的值为1
这是主线程第5次循环打印,打印的值为1
这是主线程第6次循环打印,打印的值为1
这是主线程第7次循环打印,打印的值为1
这是主线程第8次循环打印,打印的值为1
这是主线程第9次循环打印,打印的值为1
这是主线程第10次循环打印,打印的值为1
这是子线程第1次循环打印,打印的值为2
这是子线程第2次循环打印,打印的值为2
这是子线程第3次循环打印,打印的值为2
这是子线程第4次循环打印,打印的值为2
这是子线程第5次循环打印,打印的值为
这是主线程第1次循环打印,打印的值为2
这是主线程第2次循环打印,打印的值为2
这是主线程第3次循环打印,打印的值为2
这是主线程第4次循环打印,打印的值为2
这是主线程第5次循环打印,打印的值为2
这是主线程第6次循环打印,打印的值为2
这是主线程第7次循环打印,打印的值为2
这是主线程第8次循环打印,打印的值为2
这是主线程第9次循环打印,打印的值为2
这是主线程第10次循环打印,打印的值为2

  在JDK1.5之后提供了新的等待唤醒机制来更好的解决问题,由于使用量的封装,所以不需要修改test代码只需要对资源类进行修改即可

 

//定义一个资源类
//具有两个互斥的方法 outSum outMain
class Resource{
	//定义一个标记表示现在该子线程执行还是main线程执行
	boolean isMain = false;
	Lock lock = new ReentrantLock();
	//在一个锁上门挂两个减少器,主要用于代理Object.wait() Object.notfiy() 
		//提供唤醒对方的机制
	final Condition sub = lock.newCondition();
	final Condition main = lock.newCondition();
			
		public  void outSub(int j){
			lock.lock(); //获取锁
			while(isMain){ //如果是main线程执行呢 就wait自己 使用while双重保险
				try {
					sub.await(); //等待 
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			try {
				for(int i=1;i<=5;i++){
					System.out.println("这是子线程第"+i+"次循环打印,打印的值为"+j);
				}
				isMain=true;
				//this.notify();//唤醒线程 这里不一定唤醒的是main线程 只是随机唤醒线程池中的一个线程对象 
				//为了保证一定能够唤醒到对方 所以调用notifyAll()唤醒线程池中的所以休眠的线程
//				this.notifyAll();
				main.signal(); //唤醒main线程
			} finally{
				lock.unlock();
			}
		}
		
		public  void outMain(int j){
			lock.lock(); //获取锁
			while(!isMain){ //如果是子线程执行就wait 使用while双重保险
				try {
					main.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			try {
				for(int i=1;i<=10;i++){
					System.out.println("这是主线程第"+i+"次循环打印,打印的值为"+j);
				}
				isMain=false;
				//this.notify();//唤醒线程 这里不一定唤醒的是main线程 只是随机唤醒线程池中的一个线程对象 
				//为了保证一定能够唤醒到对方 所以调用notifyAll()唤醒线程池中的所以休眠的线程
//				this.notifyAll();
				sub.signal();
			}finally{
				lock.unlock();
			}
			
		}
	
}

 

 

你可能感兴趣的:(多线程,线程通信)