Java 自旋锁的替代方案

Java 自旋锁的替代方案,java,user-interface,synchronization,spinlock,Java,User Interface,Synchronization,Spinlock,我使用以下自旋锁方法: while(!hasPerformedAction()){ //wait for the user to perform the action //can add timer here too } setHasPerformedAction(false); return getActionPerfomed(); 这基本上是等待用户执行操作,然后返回操作。目前有些东西在继续之前要求用户回答,这就是为什么我要等到收到输入。然而,我想知道这是否是低效的,如

我使用以下自旋锁方法:

while(!hasPerformedAction()){
    //wait for the user to perform the action
    //can add timer here too
}

setHasPerformedAction(false);

return getActionPerfomed();
这基本上是等待用户执行操作,然后返回操作。目前有些东西在继续之前要求用户回答,这就是为什么我要等到收到输入。然而,我想知道这是否是低效的,如果我们正在等待一段时间,即在您的标记中有一个gui,那么我将假设操作是在gui中完成的

建议这样做的方法是让主线程自己进入睡眠状态,可能使用wait,等待GUI操作在执行操作后通知它重新进入唤醒状态。

您的标记中有一个GUI,因此我假设该操作是在GUI中完成的


执行此类操作的推荐方法是让主线程自己进入睡眠状态(可能使用wait),然后让GUI操作在执行操作后通知它重新进入唤醒状态。

在java.util.concurrent.locks中有一些类用于此。看一看类锁支持


Mike

java.util.concurrent.locks中有一些此类。看一看类锁支持


关于Mike,事实上,这不仅效率低下,甚至不能保证有效,因为在您显示的代码中没有before edge。要创建一个应用程序,您需要执行以下操作之一:

访问可变变量 在共享资源上同步 使用并发utils锁。 正如在另一篇评论中提到的,最简单的解决方案就是确保您的标志是一个易失性变量,并在循环中抛出一个短睡眠

但是,最好是对共享变量进行同步/等待/通知

您需要阅读的方法有和。有关如何使用这些工具的更好说明,请阅读。下面显示了一个示例代码段

线程1

线程2


事实上,这种方法不仅效率低下,甚至不能保证有效,因为在所显示的代码中没有before。要创建一个应用程序,您需要执行以下操作之一:

访问可变变量 在共享资源上同步 使用并发utils锁。 正如在另一篇评论中提到的,最简单的解决方案就是确保您的标志是一个易失性变量,并在循环中抛出一个短睡眠

但是,最好是对共享变量进行同步/等待/通知

您需要阅读的方法有和。有关如何使用这些工具的更好说明,请阅读。下面显示了一个示例代码段

线程1

线程2


你写的是所谓的忙循环,你永远不应该这样做

您可能希望继续这样做,但至少要睡一会儿,以免忙于循环,这仍然不是那么好:

while( !hasPerformedAction() ) {
    (sleep a bit here)
}
另一种方法是将用户操作排入阻塞队列:然后您可以简单地使用queue.take,队列实现将为您解决这个问题


另一种方法是使用回调来通知用户操作。

您编写的内容称为忙循环,您永远不应该这样做

您可能希望继续这样做,但至少要睡一会儿,以免忙于循环,这仍然不是那么好:

while( !hasPerformedAction() ) {
    (sleep a bit here)
}
另一种方法是将用户操作排入阻塞队列:然后您可以简单地使用queue.take,队列实现将为您解决这个问题


另一种方法是使用回调来通知用户操作。

java教程中有一堂关于java并发性的好课:

如果要从另一个线程修改UI,请记住执行以下操作:

SwingUtils.invokeLater(actionToInvoke);

java教程中有一堂关于java并发性的好课:

如果要从另一个线程修改UI,请记住执行以下操作:

SwingUtils.invokeLater(actionToInvoke);

用户在哪里执行操作?在命令行上,在GUI中?用户在哪里执行操作?在命令行上,在GUI中?代码是否类似于if!hasActionPerformed{wait;}在Gui组件的操作侦听器中,我们设置了actionPerformed和调用notifyAll?Gui在事件调度线程上执行时会导致错误吗?老实说,我从来没有尝试过这一点,我会从您所说的开始,通过尝试和错误来实现这一点。我的首选解决方案是主线程离开,即运行到完成,用户操作启动执行下一个处理步骤的线程。更干净。我看到Paul Wagland已经解释了等待/通知更好,如果你想这样做。代码会像if!hasActionPerformed{wait;}在Gui组件的操作侦听器中,我们设置了actionPerformed和调用notifyAll?Gui在事件调度线程上执行时会导致错误吗?老实说,我从来没有尝试过这一点,我会从w开始尝试出错

你说的帽子。我的首选解决方案是主线程离开,即运行到完成,用户操作启动执行下一个处理步骤的线程。更干净。我知道Paul Wagland解释了等待/通知更好,如果你想这样做的话。人们有点看不起简单的睡眠解决方案,但实际上,1毫秒或10毫秒的睡眠会将观察到的系统负载从100%降至接近0%。原因是该应用程序大部分时间都在睡觉:1毫秒比起床、查看标志和返回睡眠所需的时间要长。因此,虽然不优雅,但这是一个非常实用的解决方案,只需要很少的更改。尽管如此,这里提供的所有其他解决方案都更优雅、更高效、更自我记录、更健壮。。。更好@卡尔:问题是这实际上不能保证工作,因为不能保证线程会看到另一个线程所做的更新。大多数情况下,它正好起作用,但这是不可靠的。@Paul:当然,你是对的;这就是为什么我使用wait/notify将OP指向您的机制。我和老发烧友都回想起Java多线程要简单得多的时代。考虑到这个解决方案不起作用的危险,我现在不情愿地收回我对这个解决方案的赞成票。人们有点看不起这个简单的睡眠解决方案,但实际上,1毫秒或10毫秒的睡眠会将观察到的系统负载从100%降到接近0%。原因是该应用程序大部分时间都在睡觉:1毫秒比起床、查看标志和返回睡眠所需的时间要长。因此,虽然不优雅,但这是一个非常实用的解决方案,只需要很少的更改。尽管如此,这里提供的所有其他解决方案都更优雅、更高效、更自我记录、更健壮。。。更好@卡尔:问题是这实际上不能保证工作,因为不能保证线程会看到另一个线程所做的更新。大多数情况下,它正好起作用,但这是不可靠的。@Paul:当然,你是对的;这就是为什么我使用wait/notify将OP指向您的机制。我和老发烧友都回想起Java多线程要简单得多的时代。考虑到这个解决方案不起作用的危险,我现在不情愿地收回我对它的支持票。谢谢,在这个例子中,共享变量是一个布尔值,我们不能在一个可变布尔值上同步,所以我仍然有点担心stuck@Aly:您可以使用不同的变量作为第二个共享状态,使用AtomicBoolean而不是bool,或者简单地在循环中插入一个sleep,因为如果布尔值是可变的,那么它是线程安全的。这不是真正的自旋锁-它是一个常规锁。线程1调用wait的那一刻——它不再是可调度的,因此它将被切换到可调度的线程。因此,线程1在CPU缓存中的所有内容都消失了。还是我遗漏了什么?@PaulWagland你应该澄清1、2和3是或。@quixver事实上这不是旋转锁,这是一个替代方案,并且可以说是一个更好的方案,因为你提到的确切原因。谢谢,本例中的共享变量是一个布尔值,我们不能在易变布尔值上同步,所以我还是有点stuck@Aly:您可以使用另一个变量作为第二个共享状态,使用AtomicBoolean而不是bool,或者简单地在循环中插入睡眠,因为如果布尔值是可变的,那么它是线程安全的。这不是真正的自旋锁-它是常规锁。线程1调用wait的那一刻——它不再是可调度的,因此它将被切换到可调度的线程。因此,线程1在CPU缓存中的所有内容都消失了。还是我遗漏了什么?@PaulWagland你应该澄清1、2和3是或。@quixver事实上,这不是旋转锁,这是一个替代方案,而且可以说是一个更好的方案,因为你提到的确切原因。