java多线程间数据通信方法

本文介绍Java多线程访问共享的数据的三种策略

  1. 第一种:使用synchronized同步方法共享可变数据
  2. 第二种:使用volatile共享可变数据
  3. 第三种:不共享可变数据,只共享不可变数据

首先看一段错误的代码

在下例中,main方法为主线程,backgroundThred为子线程。在主线程中,我们首先调用了子线程,并输出状态,此时应该为RUNNABLE(执行中)。为了使子线程能够允许2秒我们调用了sleep()方法来睡眠2秒,2秒后将stopFlag置为true。这时我们预期,子线程的while方法应该停止,所以再次睡眠两秒后,输出的backgroundThred状态应该为TERMINATED(终止)。

※插播Thread的六种状态
NEW:未启动的线程。
RUNNABLE:在JVM上执行的状态。
BLOCKED:阻塞状态,等待锁释放的状态。
WAITING:无期限等待其它线程的状态。
TIMED_WAITING:等待固定的停止时间或是等待其它线程实行的状态。
TERMINATED:执行结束状态。

package test;

import java.util.concurrent.TimeUnit;

public class Test {
	
	private static boolean stopFlag = false;
	
	public static void main(String[] args) throws InterruptedException {

		// 生成一个子线程
		Thread backgroundThred = new Thread( new Runnable() {
			
			public void run() {
				int i = 0;
				// 判断停止符号是否为真
				while( !stopFlag ){
					i++;
				}
			}
			
		});
		
		backgroundThred.start();
		System.out.println( backgroundThred.getState() );
		
		// 使主线程停止2秒,为了子线程运行2秒
		TimeUnit.SECONDS.sleep(2);
		
		// 设置线程停止的flag为true
		stopFlag = true;
	
		// 使主线程停止2秒,为了确认子线程有没有终止
		TimeUnit.SECONDS.sleep(2);
		
		System.out.println( backgroundThred.getState() );
		
    }

}

但实际执行结果,子线程并未被终止。程序无法前进。因为主线程和子线程的stopFlag并未被同步。
在这里插入图片描述

  • 使用第一种方法来修正这个问题。synchronized同步方法。

    不在主线程中执行stopFlag = true;而是创建用synchronized修饰的stopFlag的读写方法(getter和setter方法)。并在主程序中调用setter方法。
package test;

import java.util.concurrent.TimeUnit;

public class Test {
	
	private static boolean stopFlag = false;
	
	private static synchronized void setStopFlag() {
		stopFlag = true;
	}
	private static synchronized boolean getStopFlag() {
		return stopFlag;
	}
	
	public static void main(String[] args) throws InterruptedException {

		// 生成一个子线程
		Thread backgroundThred = new Thread( new Runnable() {
			
			public void run() {
				int i = 0;
				// 判断停止符号是否为真
				while( !getStopFlag() ){
					i++;
				}
			}
			
		});
		
		
		backgroundThred.start();
		System.out.println( backgroundThred.getState() );
		
		// 使主线程停止2秒,使子线程运行2秒
		TimeUnit.SECONDS.sleep(2);
		
		// 线程停止的flag
		setStopFlag();

		// 使主线程停止2秒,为了确认子线程有没有终止
		TimeUnit.SECONDS.sleep(2);
		
		System.out.println( backgroundThred.getState() );
		
    }

}

此时的实行结果,和预期一样,在调用setStopFlag()方法后,设置了stopFlag为真,因为有synchronized修饰,它的值将能在线程间通信。
实际运行结果:
在这里插入图片描述

  • 使用第二种方法来修正问题,volatile修饰符。使用volatile修饰符修饰的变量可以保证任何一个线程 在读取该域时都能看到最近刚刚被写入的值。

    使用这种方法修改,只需在错误的代码中的stopFlag变量添加volatile修饰符即可。
package test;

import java.util.concurrent.TimeUnit;

public class Test {
	
	private static volatile boolean stopFlag = false;
	
	public static void main(String[] args) throws InterruptedException {

		// 生成一个子线程
		Thread backgroundThred = new Thread( new Runnable() {
			
			public void run() {
				int i = 0;
				// 判断停止符号是否为真
				while( !stopFlag ){
					i++;
				}
			}
			
		});
		
		
		backgroundThred.start();
		System.out.println( backgroundThred.getState() );
		
		// 使主线程停止2秒,为了子线程运行2秒
		TimeUnit.SECONDS.sleep(2);
		
		// 设置线程停止的flag为true
		stopFlag = true;
	

		// 使主线程停止2秒,为了确认子线程有没有终止
		TimeUnit.SECONDS.sleep(2);
		
		System.out.println( backgroundThred.getState() );
		
    }

}

实际运行结果:
在这里插入图片描述

  • 第三种方法,是避免分享可变数据,将可变数据限制在单个线程中。即可避免该问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值