【问题标题】:IllegalMonitorStateException when using locks and conditions in Android app在 Android 应用程序中使用锁和条件时出现 IllegalMonitorStateException
【发布时间】:2021-02-07 03:38:32
【问题描述】:

我正在尝试熟悉 Java Conditions

目的:

使用以下代码,我创建了一个简单的计数器应用程序,该应用程序在单击开始后会飞向星星。您可以启动/停止、暂停/恢复计数器。

预期目的是(对我而言)允许函数在锁未锁定的情况下运行。锁被锁定的那一刻,递增应该停止,直到点击恢复。

查看这个example 和这个SO question,我尝试实现我自己的版本 - 这不成功。

问题:

点击开始,计数器运行。当我点击暂停时,锁会正确锁定,但是当执行程序可运行到达waitCondition.await(); 行时会发生java.lang.IllegalMonitorStateException

堆栈跟踪:

E/AndroidRuntime: FATAL EXCEPTION: ForkJoinPool-1-worker-1
Process: nmu.wrpv302.myapplication, PID: 11744
java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:156)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1291)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1752)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2064)
    at com.example.myapplication.MainActivity.lambda$start$2$MainActivity(MainActivity.java:61)

        ^ --------------------- PROBLEM -----------------------

    at com.example.myapplication.-$$Lambda$MainActivity$te94WnCx7dwprXfxnjJZuoEc1_8.run(Unknown Source:4)
    at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1411)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:285)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1155)
    at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1993)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1941)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

活动代码:

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) {
                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>

【问题讨论】:

    标签: java android multithreading locking


    【解决方案1】:

    您无法解锁锁然后使用它发出信号。锁必须由调用 await 或在锁条件上发出信号的线程持有。 api文档说:

    当调用此方法时,假定当前线程持有与此 Condition 关联的锁。

    没有持有该锁会导致抛出异常。

    这意味着您不能使用 isLocked 检查来确定您是否已完成等待。如果您查看链接问题上的代码示例,它会显示 put 和 take 每个都使用自己的条件,并且它们会测试指示缓冲区当前状态的变量,以确定是等待还是发出信号。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-09
      • 1970-01-01
      • 2010-12-04
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多