@Synchronized方法中的Kotlin IllegalMonitorStateException

时间:2019-05-14 10:06:26

标签: java kotlin concurrency

我正在尝试在Kotlin中重现一个老式的解决方案,以解决具有多个线程和共享内存空间的经典Consumer-Producer问题。在 Java 中,我将使用同步方法来访问共享空间。但是,在科特林中,似乎有@Synchronized注释的方法正在抛出IllegalMonitorStateException。我期望带注释的方法的行为应与 Java 中的行为完全一样,但事实并非如此。我使用synchronized(){}函数解决了问题,但我仍然感到困惑,@Synchronized无法正常工作。 为什么?

在下面的代码中,生产者通过在SynchronizedBox内增加一个计数器(长整数)来“产生”一个新值,然后消费者读取该值,然后将其打印到控制台。

无效无法正常工作的Kotlin MessageBox

package concurrency.producer_consumer

class MessageBox(var message: Long = 0): SynchronizedBox {
    private val lock = Object()
    private var empty = true

    @Synchronized
    override fun syncIncrement() {
        while (!empty) {
            lock.wait()
        }

        message++
        empty = false
        lock.notifyAll()
    }

    @Synchronized
    override fun readValue(): Long {
          while (empty) {
              lock.wait()
          }

          val readValue = message
          empty = true
          lock.notifyAll()

          return readValue
    }
}

有效的Java变体:

package concurrency.producer_consumer;

public class JBox implements SynchronizedBox {
    private long value = 0;
    private boolean empty = true;

    @Override
    public synchronized void syncIncrement() {
        while (!empty) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        value++;
        empty = false;
        notifyAll();
    }

    @Override
    public synchronized long readValue() {
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }

        empty = true;
        return value;
    }
}

实际可用的Kotlin版本:

package concurrency.producer_consumer

class MessageBox(var message: Long = 0): SynchronizedBox {
    private val lock = Object()
    private var empty = true

    override fun syncIncrement() {
        synchronized(lock) {
            while (!empty) {
                lock.wait()
            }

            message++
            empty = false
            lock.notifyAll()
        }
    }

    override fun readValue(): Long {
        synchronized(lock) {
            while (empty) {
                lock.wait()
            }

            empty = true
            lock.notifyAll()
            return message
        }
    }
}

其余代码:

消费者:     包concurrency.producer_consumer

class Consumer(private val messageBox: SynchronizedBox): Runnable {

    override fun run() {
        println("consumer thread: ${Thread.currentThread().id}: started")

        while (true) {
            println("consumer: ${messageBox.readValue()}")
            Thread.sleep(1_000)
        }
    }
}

制作人

class Producer(private val messageBox: SynchronizedBox): Runnable {

    override fun run() {
        println("producer thread: ${Thread.currentThread().id}: started")

        while (true) {
            messageBox.syncIncrement()
            Thread.sleep(1_000)
        }
    }
}

界面

package concurrency.producer_consumer

interface SynchronizedBox {
    fun syncIncrement()
    fun readValue(): Long
}

发射器

package concurrency.producer_consumer

fun main() {
    val box: SynchronizedBox = MessageBox()
    val producer1 = Producer(box)
    val consumer = Consumer(box)

    val threadP1 = Thread(producer1)
    val threadC = Thread(consumer)

    threadP1.start()
    threadC.start()
}

2 个答案:

答案 0 :(得分:0)

似乎@Synchronized确实锁定了MessageBox,并且它不仅仅因为在Kotlin中所有内容都扩展了Any而不是Java的Object。尽管不推荐在 Kotlin 中进行并发编程的方法,但解决方案是简单地扩展Object,如下所示,MessageBoxLock

package concurrency.producer_consumer.message_box

@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
class MessageBoxLock(var message: Long = 0): Object(), SynchronizedBox {

    private var empty = true

    @Synchronized
    override fun syncIncrement() {
        while (!empty) {
            wait()
        }

        message++
        empty = false
        notifyAll()
    }

    @Synchronized
    override fun readValue(): Long {

        while (empty) {
            wait()
        }

        empty = true
        notifyAll()

        return message
    }
}

答案 1 :(得分:0)

我有类似的问题,所以我没有使用 @Synchronized 注释,而是改为了 synchronized call ,如下所示,它对我有用。

private fun produce() = synchronized(lock){
   ...
}

科特林代码示例以供浏览:

fun main() {
    val producer = Producer()
    producer.name = "PRODUCER-THREAD"
    producer.start()
    val consumer = Consumer(producer)
    consumer.name = "CONSUMER-THREAD"
    consumer.start()
}

class Consumer(private val producer: Producer) : Thread() {
    override fun run() {
        try {
            while (true) {
                val data = producer.consume()
                println("$data consumed by: ${currentThread().name}")
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

class Producer : Thread() {
    override fun run() {
        try {
            while (true) produce()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun produce() = synchronized(lock) {
        while (messages.size == MAX_SIZE) lock.wait()

        val data = LocalDateTime.now().toString()
        messages.add(data)
        println("Data produced")
        lock.notify()
    }

    fun consume(): String = synchronized(lock) {
        lock.notify()
        while (messages.isEmpty()) lock.wait()

        val data = messages[0]
        println("Data consumed as: $data")
        messages.remove(data)
        return data
    }

    companion object {
        const val MAX_SIZE = 3
        val lock = Object()
        val messages = arrayListOf<String>()
    }
}