线程协作

首先引入一段代码:

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

Java中详述线程间协作的更多相关文章

  1. java中关于线程间协作所用关键字synchronized,wait,notify的用法

    wait/notify()关键字适用于一个线程通知另一个线程所需的条件状态已就绪,最常用于线程在循环中休眠直到获取特定条件的场景. 例如,一个线程一直等待直到队列中有一个组件能够处理:当组件添加到队列 ...

  2. java并发编程 线程间协作

    线程间协作 1. 等待和通知 等待和通知的标准形式 等待方: 获取对象锁 循环中判断条件是否满足,不调用wait()方法 条件满足执行业务逻辑 通知方: 获取对象所 改变条件 通知所有等待在对象的线程 ...

  3. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  4. Java并发编程(十三)线程间协作的两种方式:wait、notify、notifyAll和Condition

    在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果 ...

  5. Java多线程之线程的状态以及线程间协作通信导致的线程状态转换

      转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6561589.html  一:线程的状态以及变化图 Java中线程中状态可分为五种:New(新建状态),Ru ...

  6. Java并发编程(八)线程间协作(上)

    多线程并发执行时,不同的线程执行的内容之间可能存在一些依赖关系,比如线程一执行a()方法和c()方法,线程二执行b()方法,方法a()必须在方法b()之前执行,而方法c()必须在方法b()之后执行.这 ...

  7. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  8. Java并发--线程间协作的两种方式:wait、notify、notifyAll和Condition

    在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界 ...

  9. java并发之线程间通信协作

    在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界 ...

随机推荐

  1. 项目中处理数据常用Excel公式

    ="'"&A1&"'," 需求:是大佬给了excel,里面是700多个单号,要我从生产的数据库中查询出每个单号对应的类型,这时需要查数据库,我决 ...

  2. IndexedDB详解

    目录 简介 IndexedDB简介 IndexedDB的使用 IndexedDB的浏览器支持 创建IndexedDB indexdb中的CURD 使用游标cursor 简介 IndexedDB是一种在 ...

  3. NP问题/NP完全问题(NP-complete problem)如何判断是否是NP完全问题

    在算法复杂度分析的过程中,人们常常用特定的函数来描述目标算法,随着变量n的增长,时间或者空间消耗的增长曲线,近而进一步分析算法的可行性(有效性). 引入了Big-O,Big-Ω,来描述目标算法的上限. ...

  4. Linux下最常用的10个文件压缩工具

    作者简介 李先生(Lemon),高级运维工程师(自称),SRE专家(目标),梦想在35岁买一辆保时捷.喜欢钻研底层技术,认为底层基础才是王道.一切新技术都离不开操作系统(CPU.内存.磁盘).网络等. ...

  5. 使用CSS的clip-path实现图片剪切效果

    最近有个业务需求:校对图片文本信息,如下图所示,当鼠标点击文本中某一行的时候,文本上会显示对应行图片同时左侧会显示对应位置的画框. clip-path 今天要说的主题是:如何剪切原图中的部分图片?(前 ...

  6. Laya 踩坑日记-人物模型穿模,模型显示不正常

    最近做游戏,人物要跑到很远的位置,z轴距离大概有20000个单位,然后就发现一个bug,到远处人物模型穿了,而且没办法改,这就尴尬了 Z轴对应值    0    100000 100000 当距离零点 ...

  7. python中列表的insert和append的效率对比

    python中insert和append方法都可以向列表中插入数据只不过append默认插入列表的末尾,insert可以指定位置插入元素. 我们来测试一下他俩插入数据的效率: 测试同时对一个列表进行插 ...

  8. 十八:SQL注入之堆叠及绕WAF

    堆叠查询注入 (双查询注入) stacked injections(堆叠注入)从名词的含义就可以看到是一堆的SQL语句一起执行,而在真实的运用中也是这样的,我们知道在mysql中,主要是命令行中,每一 ...

  9. torch.nn 的本质

    torch.nn 的本质 PyTorch 提供了各种优雅设计的 modules 和类 torch.nn,torch.optim,Dataset 和 DataLoader 来帮助你创建并训练神经网络.为 ...

  10. 万字长文爆肝 DNS 协议!

    试想一个问题,我们人类可以有多少种识别自己的方式?可以通过身份证来识别,可以通过社保卡号来识别,也可以通过驾驶证来识别,尽管我们有多种识别方式,但在特定的环境下,某种识别方法可能比另一种方法更为适合. ...