我正在尝试在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()
}
答案 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>()
}
}