java线程之间的通讯

需求

现有一个需求,有一个共享变量初始值是0,这个变量分别有一个线程操作该变量递加,另外一个线程进行读取操作,并且每次变量的值变化都必须读取并打印显示。这就是非常常见的读写分离。
下面我们用代码先编写一个读写分离的基本逻辑:

package com.jwb;

public class ThreadDemo4 {
	// 多线程的共享变量
	public static int count = 0;

	public static void main(String[] args) {
		Thread inThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (count <= 10) {
					try {
						Thread.sleep(1000); // 为了能更好观察结果,此次加了线程等待
					} catch (InterruptedException e) {
					}
					count = count + 1; // 变量递加
				}
			}
		});
		Thread outThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (count <= 10) {
					try {
						Thread.sleep(1000);// 为了能更好观察结果,此次加了线程等待
					} catch (InterruptedException e) {
					}
					System.out.println(count); // 读取变量并打印
				}
			}
		});
		inThread.start();
		outThread.start();
	}
}

打印结果为:
![](http://jiawenbin.coding.me/images/2019-05-18 pm5.09.22.png)

结果并没有满足要求,按照预期,结果应该是从1到11逐个输出,那么就需要对代码进行修改。

在修改之前,先分析一下整个过程,问题出在哪里:

  • 首先一点,对变量操作的读写是分别两个线程进行的,那么久有可能出现读取数据的线程执行2次或者多次后,写入操作的线程才执行。所以导致输出的结果会有多次打印一同个值。
  • 同理,当写入线程执行2次或者多次以后,读取线程才执行,就会造成打印的数字不连续。
  • 最后,不应该还要输出一个11,但是结果是偶然能输出11,这是线程不安全导致的问题。

找到了问题,就可以针对问题进行解决:

  • 先解决线程安全的问题,所以必须要进行线程的同步操作,将读和写都进行同步。
  • 还必须满足写入时,读取操作要暂停,同样,读取数据时,写入操作要暂停。

读写逐步,最重要的操作时让线程休眠和唤醒,既是,在读的时候如果发现还没写入完成,则休眠读线程,直到写入完成才唤醒,同理,写入的时候发现读的操作没完成,则休眠写入线程,直到读取完成才唤醒。实现读写逐步进行。
线程同步操作在之前的文章已经提到,那么java中如何实现读写逐步呢。java在Object类中提供了两个重要的成员方法,如下。

wait()、notify()

  • wait()方法:如果对象(锁对象)调用了此方法,就会使持有该对象的线程,把对象的控制权交出去(释放锁),然后让调用方法的当前线程处于等待状态。
  • notify()方法:如果对象(锁对象)调用了此方法,就会通知某一个正在等待这个对象的控制权的线程,可以继续运行(需要重新获取锁对象)。
  • 这两个方法是被定义在Object类中的,但是一定要配合synchronized一起使用,否则会出现异常。

具体的使用案例如下,我们结合上面的需求,在原来的代码上修改,并实现需求:

package com.jwb;

public class ThreadDemo5 {
	// 多线程的共享变量
	public static int count = 0;
	public static boolean flag = false; // 标记读写状态的变量,false标识正在写入

	public static void main(String[] args) {
		Object object = new Object(); // 同步锁的对象
		Thread inThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (count <= 10) {
					synchronized (object) { // 同步代码块
						if (flag) {
							try {
								object.wait(); // 如果还没有读取完成,则线程释放锁,并且休眠
							} catch (InterruptedException e) {
							}
						} else {
							count = count + 1;
							flag = true; // 改变标记
							object.notify(); // 写入完成,则释放锁,并唤醒读取线程
						}
					}
				}
			}
		});
		Thread outThread = new Thread(new Runnable() {
			@Override
			public void run() {
				while (count <= 10) {
					synchronized (object) { // 同步代码块
						if (flag) {
							System.out.println(count);
							flag = false;
							object.notify(); // 如果读取完成,释放锁,并唤醒写入线程
						} else {
							try {
								object.wait(); // 如果写入未完成,则释放锁,并休眠此线程
							} catch (InterruptedException e) {
							}
						}
					}
				}
			}
		});
		inThread.start();
		outThread.start();
	}

}

打印结果:

在这里插入图片描述

最后,还补充一下notifyAll()方法。

notifyAll()方法

notifyAll()方法的功能和notify()是一样的,唯一的区别在于,前者是通知所有的等待的线程,而后者是通知某一个正在等待的线程(随机)。此方法也是被定义在Object类中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值