Java 使用锁和条件暂停/恢复执行时出现非法监视器状态异常
我正在寻找暂停和恢复工作线程任务执行的“更好”方法 通常会使用布尔标志(如)来设置一个标志状态,工作线程在下一次迭代之前会检查该标志状态 类似于下面的代码片段Java 使用锁和条件暂停/恢复执行时出现非法监视器状态异常,java,android,multithreading,asynchronous,locking,Java,Android,Multithreading,Asynchronous,Locking,我正在寻找暂停和恢复工作线程任务执行的“更好”方法 通常会使用布尔标志(如)来设置一个标志状态,工作线程在下一次迭代之前会检查该标志状态 类似于下面的代码片段 AtomicBoolean b = new AtomicBoolean(true); Runnable r = () -> { while (true) { // check for pause while (!b.get()) {
AtomicBoolean b = new AtomicBoolean(true);
Runnable r = () -> {
while (true) {
// check for pause
while (!b.get()) {
// sleep thread then check again
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// do work
}
};
Thread p = new Thread(r);
p.start();
这当然不是一个优雅的解决方案,因此我一直在尝试锁和条件
在开始尝试使用locks&conditions之前,我们先看一下示例和文档。我将使用a和a作为工作任务中暂停/恢复状态的标志
可重入锁定
使用将暂停当前线程的执行,直到可以获取锁为止,另一种方法是,如果没有被另一个线程持有,则允许我们获取锁
条件
通过找到的示例,条件允许一个线程通过使用或(或)向另一个线程发出等待或继续执行的信号,见下文
问题:
通过查看专门针对Condition.await()
的文档以及可以调用await()
的4个条件,我相信该条件只能用于恢复任务,而不能暂停任务
与此条件相关联的锁将自动释放,当前线程出于线程调度目的被禁用,并处于休眠状态,直到发生以下四种情况之一:
- 其他一些线程为此条件调用signal()方法,而当前线程恰好被选为要唤醒的线程;或
condition.signalAll()
,从而安排它执行。i、 e.简历
但是,我看不到达到这种状态的方法,即使用条件来暂停,因为对于进入等待状态的条件,文档中说:
调用此方法时,假定当前线程持有与此条件关联的锁。由实施部门确定是否存在这种情况,如果不是,如何应对。通常,将抛出异常(例如IllegalMonitorStateException),实现必须记录该事实
我使用Android的测试示例 逻辑
public class MainActivity extends AppCompatActivity {
private TextView lblCounter;
private Button btnStartStop, btnPauseResume;
private ReentrantLock pauseLock = new ReentrantLock();
private Condition waitCondition = pauseLock.newCondition();
private ExecutorService executorService = Executors.newWorkStealingPool(4);
private AtomicBoolean stopStart = new AtomicBoolean(true);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lblCounter = findViewById(R.id.counter);
btnStartStop = findViewById(R.id.startStop);
btnPauseResume = findViewById(R.id.pauseResume);
btnStartStop.setOnClickListener(v -> {
if (stopStart.get()) {
start();
} else {
stop();
}
});
btnPauseResume.setOnClickListener(v -> {
pauseResume();
});
}
public void start() {
btnStartStop.setText("Stop");
AtomicInteger i = new AtomicInteger(0);
executorService.execute(() -> {
while (true) {
// here we check if the lock is locked, if so, we should wait await until a signal is emmited
while (pauseLock.isLocked()) {
try {
waitCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int i1 = i.incrementAndGet();
lblCounter.setText(String.valueOf(i1));
}
});
}
public void stop() {
executorService.shutdownNow();
btnStartStop.setText("Start");
}
public void pauseResume() {
if (pauseLock.isLocked()) {
pauseLock.unlock();
waitCondition.signal();
btnPauseResume.setText("Pause");
} else {
pauseLock.lock();
btnPauseResume.setText("Resume");
}
}
}
主要活动XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/counter"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="96dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="96dp"
android:text="123"
android:textColor="#000000"
android:textSize="36sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<Button
android:id="@+id/startStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="128dp"
android:text="Start"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/pauseResume"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/pauseResume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pause"
app:layout_constraintBottom_toBottomOf="@+id/startStop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/startStop" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="96dp"
android:layout_marginTop="128dp"
android:layout_marginEnd="96dp"
android:text="Counter"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
问题:
使用s&s,我如何改进工作线程暂停/恢复执行的标准布尔标志检查+我认为对于您的逻辑(管理计数器)条件、锁等的特定示例来说,似乎有些过分
final Object lock = new Object();
AtomicBoolean b = new AtomicBoolean(true);
Runnable r = () -> {
_end:
while (true) {
while (b.get()) {
if (Thread.currentThread().isInterrupted()) {
break _end;
}
// increment the counter
}
synchronized (lock) {
while (!b.get()) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break _end;
}
}
}
}
};
Thread p = new Thread(r);
p.start();
...
b.set(false); // disable
...
synchronized (lock) {
b.set(true); // enable
lock.notify(); // to notify for enabled state change
}
...
b.set(false); // disable
ExecutorService executor = Executors.newSingleThreadExecutor(); // one single thread - no race conditions between sequentially submitted tasks guaranteed
Future task;
task = executor.submit(() -> { // start increment
while (!Thread.currentThread().isInterrupted()) {
// increment the counter
}
});
...
task.cancel(true); // stop increment
...
task = executor.submit(() -> { // start increment again
while (!Thread.currentThread().isInterrupted()) {
// increment the counter
}
});
...
task.cancel(true); // stop increment
- 如果您不仅需要递增计数器,还需要等待作业准备好执行,我建议使用BlockingQueue来通信线程(工作/作业/任务队列,生产者-消费者模式)(例如,请参阅)
- 如果进行阻塞IO调用(例如,在套接字上),最好显式使用一个线程和close()(从另一个线程调用),如下所述。在关闭之前设置一个volatile标志isClosing,以知道刚刚发生的SocketException不是错误,而是关闭的副作用,可以忽略它
- 如果捕获到InterruptedException,请不要忘记使用Thread.currentThread().interrupt()还原Thread.interrupted标志,如果以后有其他人可以验证()
final ReentrantLock lock = new ReentrantLock();
final Condition enabled = lock.newCondition();
AtomicBoolean b = new AtomicBoolean(true);
Runnable r = () -> {
_end:
while (true) {
while (b.get()) {
if (Thread.currentThread().isInterrupted()) {
break _end;
}
// increment the counter
}
lock.lock();
try {
while (!b.get()) {
try {
enabled.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break _end;
}
}
} finally {
lock.unlock();
}
}
};
Thread p = new Thread(r);
p.start();
...
b.set(false); // disable
...
lock.lock(); // if you don't lock before signal, you get IllegalMonitorStateException
try {
b.set(true); // enable
enabled.signal(); // to notify for enabled state change
} finally {
lock.unlock();
}
...
b.set(false); // disable
ExecutorService executor = Executors.newSingleThreadExecutor(); // one single thread - no race conditions between sequentially submitted tasks guaranteed
Future task;
task = executor.submit(() -> { // start increment
while (!Thread.currentThread().isInterrupted()) {
// increment the counter
}
});
...
task.cancel(true); // stop increment
...
task = executor.submit(() -> { // start increment again
while (!Thread.currentThread().isInterrupted()) {
// increment the counter
}
});
...
task.cancel(true); // stop increment