Cheug's Blog

当前位置:网站首页 / JAVA / 正文

线程间的通讯

2019-04-22 / JAVA / 949 次围观 / 0 次吐槽 /

什么时候需要通信

多个线程并发执行时, 在默认情况下CPU是随机切换线程的,如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印

 

线程怎么通信

        如果希望线程等待, 就调用wait()

        如果希望唤醒等待的线程, 就调用notify();

        notify是随机唤醒一个线程

        notifyAll是唤醒所有线程

        这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用

        如果方法中没有同步锁,会有异常IllegalMonitorStateException

案例:两个线程间的通讯

public class Demo {
	public static void main(String[] args) {
		//1.创建任务对象
		MyTask task = new MyTask();
		
		//2.开启两个线程执行2个任务
		new Thread(){
			public void run() {
				while(true){
					try {
						task.task1();
					} catch (InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			};
		}.start();
		
		new Thread(){
			public void run() {
				while(true){
					try {
						task.task2();
					} catch (InterruptedException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			};
		}.start();
	}
}

class MyTask{
	
	//标识 1:可以执行任务1,2:可以执行任务2
	int flag = 1;
	
	public synchronized void task1() throws InterruptedException{
		if(flag != 1){
			this.wait();//当前线程等待
		}
		
		System.out.println("任务1");
		flag = 2;
		this.notify();//唤醒其它线程
		
	}
	
	public synchronized void task2() throws InterruptedException{
		
		if(flag != 2){
			this.wait();//线程等待
		}
		
		System.out.println("任务2");
		flag = 1;
		this.notify();//唤醒其它线程
	}
}

线程通讯的一些疑问

        1.在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法

        2.为什么wait方法和notify方法定义在Object这类中?

                    因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中

        3.sleep方法和wait方法的区别?

                    sleep方法必须传入参数,参数就是时间,时间到了自动醒来

                    wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待

                    sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡

                    wait方法在同步函数或者同步代码块中,释放锁


JDK1.5新特性互斥锁

        ReentrantLock介绍

                使用ReentrantLock类也可以实现同步加锁

                ReentrantLock[互斥锁],使用lock()unlock()方法进行同步

        使用ReentrantLock类使用要点

                使用ReentrantLock类的newCondition()方法可以获取Condition对象

                需要等待的时候使用Conditionawait()方法, 唤醒的时候用signal()方法

                不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

案例:

/**
 * 互斥锁的使用步骤
 * 1.创建互斥锁对象
 * 2.创建3个Condition
 * 3.加锁、解锁
 * 4.线程等待-Condition的await方法
 * 5.线程唤醒-Condition的signal方法
 * @author gyf
 *
 */
class MyTask{
	//创建互斥锁对象
	ReentrantLock rl = new ReentrantLock();
	//创建3个Condition
	Condition c1 = rl.newCondition();
	Condition c2 = rl.newCondition();
	Condition c3 = rl.newCondition();
	
	//标识 1:可以执行任务1,2:可以执行任务2, 3:可以执行任务3 
	int flag = 1;
	
	public void task1() throws InterruptedException{
	 rl.lock();//加锁
			if(flag != 1){
				c1.await();//当前线程等待
			}
			
			System.out.println("任务1");
			flag = 2;
			
			//指定唤醒线程2
			c2.signal();
	 rl.unlock();//解锁
	}
	
	public void task2() throws InterruptedException{
	  rl.lock();	
			if(flag != 2){
				c2.await();//线程等待
			}
			
			System.out.println("任务2");
			flag = 3;
			
			//唤醒线程3
			c3.signal();
	  rl.unlock();
	}
	
	public void task3() throws InterruptedException{
	 rl.lock();
			if(flag != 3){
				c3.await();//线程等待
			}
			
			System.out.println("任务3");
			flag = 1;
			
			//唤醒线程1
			c1.signal();
	 rl.unlock();
	}
}


Powered By Cheug's Blog

Copyright Cheug Rights Reserved.