java多线程间数据通信方法

    技术2025-02-04  5

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

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

    首先看一段错误的代码

    在下例中,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() ); } }

    实际运行结果:

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

    Processed: 0.008, SQL: 9