线程间协作


一、线程间协作:线程间的协作是为了解决线程执行时谁先谁后的为题,可以合理的确定多线程中线程的先后执行顺序

二、线程协作使用的方法:谁调用了wait方法,谁就必须调用notifynotifyAll方法,并且“谁”是对象锁

wait():对象锁调用了wait()方法,会使持有该对象锁的对象处于阻塞状态并释放对象锁的控制权,直到其他线程中的对象锁调用notify()方法或notifyAll()方法时等待此对象锁的线程才会被唤醒

notify():对象锁调用notify()方法就会唤醒在此对象锁上等待的单个线程

notifyAll():对象锁调用notifyAll()方法就会唤醒在此对象锁上等待的所有线程,只是线程结束了阻塞状态,进入了就绪状态,需要同其他的线程重新竞争CPU的执行权

三、使用Object类中的wait()notify()notifyAll()三个方法需要注意以下几点:

1.wait()方法需要和notify()notifyAll()方法中的一个配对使用,且wait方法与notify()notifyAll()方法配对使用时不能在同一个线程中

class CounterThread extends Thread {

	private Object lockObj;

	public CounterThread(Object lockObj) {
		this.lockObj = lockObj;
	}

	@Override
	public void run() {
		synchronized (lockObj) {
			try {
				System.out.println("wait方法被执行!");
				lockObj.wait();//执行该方法的线程进入阻塞,因此对此程序而言,下面代码永远不会被执行
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		synchronized (lockObj) {
			System.out.println("notify方法被执行");
			lockObj.notify();
		}
	}
}

//测试类
public class Test {

	public static void main(String[] args) {
		Object lockObj = new Object();
		new CounterThread(lockObj).start();
	}
}

2.wait()方法、notify()方法和notifyAll()方法必须在同步方法或者同步代码块中使用,否则出现IllegalMonitorStateException 异常

3.调用wait()方法、notify()方法和notifyAll()方法的对象必须和同步锁对象是一个对象

public class Watch {
	String currentTime;
	DisplayThread displayThread = new DisplayThread();
	
	public static void main(String[] args) {
		new Watch().displayThread.start();
	}

	/**
	 * 该线程负责显示时间
	 */
	class DisplayThread extends Thread{
		
		@Override
		public synchronized void run() {//相当于synchronized(this),此时的this代表DisplayThread对象,与TimeThread对象不是同一个对象,不能实现线程间的协作
			new TimeThread().start();
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(currentTime);
		}
	}

	/**
	 * 该线程负责获取时间
	 */
	class TimeThread extends Thread{
		
		@Override
		public synchronized void run() {//相当于synchronized(this),此时的this代表TimeThread对象,与DisplayThread对象不是同一个对象,不能实现线程间的协作
			try {
				sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			String pattern = "yyyy-MM-dd HH:mm:ss";
			SimpleDateFormat sdf = new SimpleDateFormat(pattern);
			currentTime = sdf.format(new Date());
			this.notify();
		}
	}
}

四、sleep()方法和wait()方法区别

执行sleep()方法的线程会进入阻塞,但它不会执行释放对象锁,其他的线程依旧无法执行临界区中的代码

public class Test {

	public static void main(String[] args) {
		Object lockObj = new Object();
		new PrintThread("1号打印机",lockObj).start();
		new PrintThread("2号打印机",lockObj).start();
	}
}

class PrintThread extends Thread {
	
	private Object lockObj;

	public PrintThread(String threadName, Object lockObj) {
		super(threadName);
		this.lockObj = lockObj;
	}

	@Override
	public void run() {
		synchronized (lockObj) {//当某台打印机执行临界区中的代码,输出线程名后由于调用了sleep方法,从而使得该打印机线程阻塞30秒,在这30秒期间因该打印机线程仍然持有对象锁,从而导致另一台打印机线程只能在30秒后才能执行临界区中的代码。
			System.out.println(getName()+":"+new Date());
			try {
				sleep(30000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//打印结果:
//1号打印机:Sat Aug 24 17:56:25 CST 2019
//2号打印机:Sat Aug 24 17:56:55 CST 2019

//确实如我们所想,由于调用了sleep方法,从而使得该打印机线程阻塞30秒,在这30秒期间因该打印机线程仍然持有对象锁,从而导致另一台打印机线程只能在30秒后才能执行临界区中的代码。

执行wait()方法的线程会进入阻塞的状态,同时释放对象锁,其他线程可以执行临界区域中的代码

public class Test {

	public static void main(String[] args) {
		Object lockObj = new Object();
		new PrintThread("1号打印机",lockObj).start();
		new PrintThread("2号打印机",lockObj).start();
	}
}

class PrintThread extends Thread {
	
	private Object lockObj;

	public PrintThread(String threadName, Object lockObj) {
		super(threadName);
		this.lockObj = lockObj;
	}

	@Override
	public void run() {
		synchronized (lockObj) {
			System.out.println(getName());
			try {
				lockObj.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//打印结果:
//1号打印机Sat Aug 24 18:00:32 CST 2019
//2号打印机Sat Aug 24 18:00:32 CST 2019

//证明在对象锁执行wait方法时,会释放对象锁的控制权,其他线程也可以执行临界区中的代码

五、wait()方法

class CounterThread extends Thread {

	private Object lockObj;

	public CounterThread(String threadName, Object lockObj) {
		super(threadName);
		this.lockObj = lockObj;
	}

	@Override
	public void run() {
		int i = 1;
		while (true) {
			synchronized (lockObj) {
				System.out.println(getName() + ":" + i);
				try {
					lockObj.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				i++;
			}
		}
	}
}

public class Test {

	public static void main(String[] args) {
		Object lockObj = new Object();
		new CounterThread("1号计数器",lockObj).start();
		new CounterThread("2号计数器",lockObj).start();
	}
}

//尽管此处是死循环,但由于对象锁lockObj调用了wait()方法,使得分别持有该lockObj对象锁的“1号计数器”线程和“2号计数器”线程处于线程等待状态,所以循环并没有继续下去

六、notify()方法

public class Watch {
	String currentTime;
	DisplayThread displayThread = new DisplayThread();
	private static final Object lockObject = new Object();
	
	public static void main(String[] args) {
		new Watch().displayThread.start();
	}

	/**
	 * 该线程负责显示时间
	 */
	class DisplayThread extends Thread{
		
		@Override
		public void run() {
			synchronized (lockObject) {
				new TimeThread().start();
				try {
					lockObject.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(currentTime);//输出结果一定不为null
			}
		}
	}

	/**
	 * 该线程负责获取时间
	 */
	class TimeThread extends Thread{
		
		@Override
		public void run() {
			synchronized (lockObject) {
				try {
					sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				String pattern = "yyyy-MM-dd HH:mm:ss";
				SimpleDateFormat sdf = new SimpleDateFormat(pattern);
				currentTime = sdf.format(new Date());
				lockObject.notify();
			}
		}
	}	
}

七、notifyAll()方法

class CounterThread extends Thread {

	private Object lockObj;

	public CounterThread(String threadName, Object lockObj) {
		super(threadName);
		this.lockObj = lockObj;
	}

	@Override
	public void run() {
		int i = 1;
		while (true) {
			synchronized (lockObj) {
				System.out.println(getName() + ":" + i);
				try {
					lockObj.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				i++;
			}
		}
	}
}

class NotifyAllThread extends Thread {

	private Object lockObj;

	public NotifyAllThread(Object lockObj) {
		this.lockObj = lockObj;
	}

	@Override
	public void run() {
		synchronized (lockObj) {
			System.out.println("notifyAll方法执行完毕");
			lockObj.notifyAll();
		}
	}
}

public class Test {

	public static void main(String[] args) {
		Object lockObj = new Object();
		new CounterThread("1号计数器", lockObj).start();
		new CounterThread("2号计数器", lockObj).start();
		
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		new NotifyAllThread(lockObj).start();
	}
}


版权声明:本文为weixin_42867975原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_42867975/article/details/100055026