Java中详述线程间协作

线程协作

首先引入一段代码:

package 线程间数据共享;

import java.util.Date;

public class Watch {

	private static String time;
	
	static class Display extends Thread{
		Time timeThread;
		
		Display(Time time) {
			this.timeThread=time;
		}
		
		@Override
		public void run() {
			if(time==null){
			
			}
			System.out.println(time);
		}
	}
	
	static class Time extends Thread{
		
		@Override
		public void run() {
			time = new Date().toString();
		}
	}
	
	public static void main(String[] args) {
		Time time = new Time();
		time.start();
		new Display(time).start();
	}
}

结果显示为:
在这里插入图片描述
之后我们在if(time==null){ }语句代码块中添加,使其能够正常输出(输出结果不为null)
代码展示为:

package 线程间数据共享;

import java.util.Date;

public class Watch {

	private static String time;
	
	static class Display extends Thread{
		Time timeThread;
		Object object;
		
		Display(Time time) {
			this.timeThread=time;
		}
		
		@Override
		public void run() {
			if(time==null) {
				try {
					sleep(1500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(time);
		}
	}
	
	static class Time extends Thread{
		
		@Override
		public void run() {
			time = new Date().toString();
		}
	}
	
	public static void main(String[] args) {
		final Object OBJECT = new Object();
		Time time = new Time();
		time.start();
		new Display(time).start();
	}
}

结果显示为:
在这里插入图片描述
我们通过sleep()方法实现了正常的输出,但是在实际操作过程中,我们无法判断sleep()中所需要限制的时间,因此我们需要通过另一种方法来实现:即使用join()方法,代码展示为:

package 线程间数据共享;

import java.util.Date;

public class Watch {

	private static String time;
	
	static class Display extends Thread{
		Time timeThread;
		Object object;
		
		Display(Time time) {
			this.timeThread=time;
		}
		
		@Override
		public void run() {
			if(time==null) {
				try {
					timeThread.join();//线程A执行“已死”线程B所调用的jion方法,则线程A不会阻塞。
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(time);
		}
	}
	
	static class Time extends Thread{
	
		@Override
		public void run() {
			time = new Date().toString();
		}
	}
	
	public static void main(String[] args) {
		final Object OBJECT = new Object();
		Time time = new Time();
		time.start();
		new Display(time).start();
	}
}

结果显示为:
在这里插入图片描述
如果不使用join()方法,我们还可以选择使用synchronized ()关键字的形式实现,代码展示为:

package 线程间数据共享;

import java.util.Date;

public class Watch {

	private static String time;
	
	static class Display extends Thread{
		Time timeThread;
		Object object;
		
		Display(Time time,Object object) {
			this.timeThread=time;
			this.object=object;
		}
		
		@Override
		public void run() {
			if(time==null) {
				synchronized (object) {
					try {//如果没有synchronized关键字,则会出现报错
						object.wait();//线程执行该方法,则该线程阻塞,知道调用notify方法
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println(time);
		}
	}
	
	static class Time extends Thread{
		Object object;
		Time(Object object){
			this.object=object;
		}
		
		@Override
		public void run() {
			time = new Date().toString();
			synchronized (object) {
				object.notify();
			}
		}
	}
	
	public static void main(String[] args) {
		final Object OBJECT = new Object();
		Time time = new Time(OBJECT);
		time.start();
		new Display(time,OBJECT).start();
	}
}

结果显示为:
在这里插入图片描述

Object类中解决线程间协作的方法

synchronized关键字只是起到了多个线程“串行”执行临界区中代码的作用,但是哪个线程先执行,哪个线程后执行依无法确定。Object类中的wait()、notify()和notifyAll()三个方法解决了线程间的协作问题,通过这三个方法的“合理”使用可以确定多线程中线程的先后执行顺序:
1、wait():对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,直到在其他线程中该对象锁调用notify()方法或notifyAll()方法时等待此对象锁的线程才会被唤醒。
2、notify():对象锁调用notify()方法就会唤醒在此对象锁上等待的单个线程。
3、notifyAll():对象锁调用notifyAll()方法就会唤醒在此对象锁上等待的所有线、程;调用notifyAll()方法并不会立即激活某个等待线程,它只能撤销等待线程的中断状态,这样它们就能够在当前线程退出同步方法或同步代码块法后与其它线程展开竞争,以争取获得资源对象来执行。

谁调用了wait方法,谁就必须调用notify或notifyAll方法,并且“谁”是对象锁。

使用Object类中的wait()、notify()和notifyAll()三个方法需要注意以下几点:
1、wait()方法需要和notify()或notifyAll()方法中的一个配对使用,且wait方法与notify()或notifyAll()方法配对使用时不能在同一个线程中(参见代码1)。
2、wait()方法、notify()方法和notifyAll()方法必须在同步方法或者同步代码块中使用,否则出现IllegalMonitorStateException 异常。
3、调用wait()方法、notify()方法和notifyAll()方法的对象必须和同步锁对象是一个对象。

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

1、sleep()方法被调用后当前线程进入阻塞状态,但是当前线程仍然持有对象锁,在当前线程sleep期间,其它线程无法执行sleep方法所在临界区中的代码。

package 线程间数据共享;

import java.util.Date;

public class sleep和wait区别 {

		public static void main(String[] args) {
			Object lockObj = new Object();
			new PrintThread("1号打印机"+new Date(),lockObj).start();
			new PrintThread("2号打印机"+new Date(),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 {
					sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
}

结果显示为:
在这里插入图片描述
由此我们可以看出:当某台打印机执行临界区中的代码,输出线程名后由于调用了sleep方法,从而使得该打印机线程阻塞3秒,在这3秒期间因该打印机线程仍然持有对象锁,从而导致另一台打印机线程只能在3秒后才能执行临界区中的代码。(但是所执行的时间是相同的)
2、对象锁调用了wait()方法会使当前持有该对象锁的线程处于线程等待状态同时该线程释放对对象锁的控制权,在当前线程处于线程等待期间,其它线程可以执行wait方法所在临界区中的代码。

package 线程间数据共享;

import java.util.Date;

public class sleep和wait区别 {

		public static void main(String[] args) {
			Object lockObj = new Object();
			new PrintThread("1号打印机"+new Date(),lockObj).start();
			new PrintThread("2号打印机"+new Date(),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();
				}
			}
		}
	}

结果显示为:
在这里插入图片描述

wait()方法

代码一:

package 线程间数据共享;
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();
	}
}


结果显示为:
在这里插入图片描述
尽管while(){xxx}循环是死循环,但由于对象锁lockObj调用了wait()方法,使得分别持有该lockObj对象锁的“1号计数器”线程和“2号计数器”线程处于线程等待状态,所以循环并没有继续下去
代码二:

package 线程间数据共享;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class sleep和wait区别 {
	
		public static void main(String[] args) {
			TimeThread timeThread = new TimeThread ();
			timeThread.start();

			synchronized (timeThread) {
				try {
					timeThread.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println("main方法");//由于主线程进入了阻塞状态,所以该行代码不执行
		}
	}

	class TimeThread extends Thread{

		@Override
		public void run() {
			DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
			while (true) {
				String currentTime = dateFormat.format(new Date());
				System.out.println(currentTime);
				try {
					sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

结果显示为:
在这里插入图片描述
为什么时间线程没有进入阻塞状态呢?
timeThread变量所代表的对象充当对象锁,由于该代码在main方法中,也就是说将来由主线程执行临界区中的代码,也就是说主线程是持有对象锁的线程,主线程执行完“timeThread.wait()”代码后进入阻塞状态,是主线程进入阻塞状态而非时间线程,这也是main方法中“System.out.println(“main方法”);”代码不执行的原因。

notify()方法和notifyAll()方法

notify()方法

import java.text.SimpleDateFormat;
import java.util.Date;

public class ElectronicWatch {
	
	String currentTime;
	DisplayThread displayThread = new DisplayThread();
	private static final Object lockObject = new Object();
	
	public static void main(String[] args) {
		new ElectronicWatch().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);
			}
		}
	}

	/**
	 * 该线程负责获取时间
	 */
	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()方法

package 线程间数据共享;
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();
	}
}

结果显示为:
在这里插入图片描述

如果调用的是notify方法,则只会唤醒在lockObj对象锁上等待的两个线程中的一个;
而调用notifyAll方法则会全部唤醒,尽管只调用一次
http://www.dtmao.cc/news_show_676906.shtml

posted on 2021-02-05 23:41  1763392456  阅读(75)  评论(0编辑  收藏  举报

导航