try (
ExecutorService executorService = Executors.newVirtualThreadExecutor() ;
)
{
AtomicBoolean flag = new AtomicBoolean( true );
// This task, when run, will immediately report the flag.
Runnable task1 = ( ) -> System.out.println( "First task reporting flag = " + flag.get() + ". " + Instant.now() );
// This task, when run, will wait several seconds, then report the flag. Meanwhile, code below waits a shorter time before *changing* the flag.
Runnable task2 = ( ) -> {
try { Thread.sleep( Duration.ofSeconds( 10 ) ); } catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "Second task reporting flag = " + flag.get() + ". " + Instant.now() );
};
executorService.submit( task1 );
executorService.submit( task2 );
// Wait for first task to complete, so sleep here briefly. But wake before the sleeping second task awakens.
try { Thread.sleep( Duration.ofSeconds( 5 ) ); } catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "INFO - Original thread waking up, and setting flag to false. " + Instant.now() );
flag.set( false );
}
// At this point, with Project Loom technology, the flow-of-control blocks until the submitted tasks are done.
// Also, the `ExecutorService` is automatically closed/shutdown by this point, via try-with-resources syntax.
System.out.println( "INFO - Tasks on background threads are done. The `AtomicBoolean` and threads are gone." + Instant.now() );
First task reporting flag = true. 2021-01-05T06:42:17.367337Z
INFO - Original thread waking up, and setting flag to false. 2021-01-05T06:42:22.367456Z
Second task reporting flag = false. 2021-01-05T06:42:27.369782Z
INFO - Tasks on background threads are done. The `AtomicBoolean` and threads are gone.2021-01-05T06:42:27.372597Z
public class Test {
class Control {
public volatile boolean flag = false;
}
final Control control = new Control();
class T1 implements Runnable {
@Override
public void run() {
while ( !control.flag ) {
}
}
}
class T2 implements Runnable {
@Override
public void run() {
while ( !control.flag ) {
}
}
}
private void test() {
T1 main = new T1();
T2 help = new T2();
new Thread(main).start();
new Thread(help).start();
}
public static void main(String[] args) throws InterruptedException {
try {
Test test = new Test();
test.test();
} catch (Exception e) {
e.printStackTrace();
}
}
}
7条答案
按热度按时间iszxjhcz1#
您可以使用锁定变量“a”和“b”并同步它们,以便以相反的顺序锁定“关键部分”。例如,通知“a”,然后锁定“b”,“打印”,通知“b”,然后锁定“a”。
请参考以下代码:-
}
输出:-1 a 2 b 3 a 4 b 5 a 6 b 7 a 8 b 9 a 10 b
fkvaft9z2#
使用
static
对你的案子没有帮助。使用
synchronize
当一个变量被另一个线程使用时锁定它。你应该使用
volatile
关键字保持变量在所有线程之间更新。使用volatile是使类线程安全的另一种方法(如同步的原子 Package 器)。线程安全意味着一个方法或类示例可以被多个线程同时使用而不会出现任何问题。
kx7yvsdv3#
两者
T1
以及T2
可以引用包含此变量的类。你可以让这个变量变为易变的,这意味着
对该变量的更改在两个线程中立即可见。
有关更多信息,请参阅本文。
volatile变量共享synchronized的可见性特性,但没有原子性特性。这意味着线程将自动看到volatile变量的最新值。它们可以用来提供线程安全性,但只能在非常有限的情况下使用:那些不在多个变量之间或变量的当前值和未来值之间施加约束的情况。
并注意使用
volatile
与更复杂的共享状态的方法相比。jbose2ul4#
使其在
T1
以及T2
可以使这两个类包含对包含变量的对象的引用。如果要在线程运行时修改变量,则需要考虑同步。最佳方法取决于您的具体要求,但主要选项如下:
使变量
volatile
;把它变成一个
AtomicBoolean
;对使用它的代码使用全面的同步。
14ifxucb5#
原子布尔
npe简洁的回答总结了你的三个选择。我将为下面列出的第二项添加一些示例代码:
AtomicBoolean
.你可以想到
AtomicBoolean
类作为在boolean
价值观。如果你示例化
AtomicBoolean
只需一次,就不必担心java内存模型中的可见性问题volatile
作为解决方案(另一个答案中的第一项)。另外,您不必关心同步(另一个答案中的第三项),因为AtomicBoolean
执行保护多线程访问其内部布尔值的功能。让我们看一些示例代码。
首先,在现代java中,我们通常不处理
Thread
直接上课。我们现在有了executors框架来简化线程的处理。下面的代码使用的是ProjectLoom技术,是java的未来版本。现在提供了初步的构建,构建在早期的accessjava16之上。这使得编码更加简单,并且
ExecutorService
存在AutoCloseable
为了方便使用try-with-resources语法。但织布机项目与这个答案的要点无关;它只会使代码更简单,更容易理解为“结构化并发”。这里的想法是我们有三个线程:原始线程,加上
ExecutorService
这将创建另外两个线程。两个新线程都报告了AtomicBoolean
. 第一个新线程立即执行此操作,而另一个线程在报告前等待10秒。同时,我们的主线程休眠5秒,唤醒,更改AtomicBoolean
对象所包含的值,然后等待第二个线程唤醒并完成其工作,此时该线程上的报告已更改AtomicBoolean
包含的值。当我们在每个事件之间设置秒数时,这仅仅是为了戏剧性的演示。真正的问题是,这些线程可能碰巧尝试访问AtomicBoolean
同时,但该对象将以线程安全的方式保护对其内部布尔值的访问。防止同时访问是Atomic…
班级。方法,例如
AtomicBoolean#get
以及AtomicBoolean#set
被构建为线程安全的,以在内部保护对嵌套的布尔值的访问。阅读其他各种方法。运行时:
专业提示:在java中使用线程代码时,一定要学习brian goetz等人的优秀著作《java并发实践》。
eaf3rand6#
除了其他建议之外,您还可以在控件类中 Package 该标志,并在父类中生成该标志的最终示例:
oyt4ldly7#
将其设置为静态可以解决此问题。
引用其他线程中的主线程并使该变量可见