I have to write a code where creating two threads - S and W. Thread S performs switching with 1000ms delay from true to false and back. Thread W is waiting "true" from thread S and outputs to the console countdown from 30sec with 100ms delay and pauses his action when thread S is swiched back to false. The condition for the termination of the operation of the threads is the achievement 0 sec of the countdown.
Firstly, I`ve created two classes - ThreadW and ThreadS. In ThreadS I created a state switch between true and false
ThreadS.kt
class ThreadS() : Thread() {
override fun run()
{
var isTrue:Boolean = true
while (true)
{
if (isTrue)
{
Thread.sleep(1000)
isTrue = false
println(isTrue)
}
else
{
Thread.sleep(1000)
isTrue = true
println(isTrue)
}
}
}
}
In the main file I created coroutine and creating a class instance of ThreadS then starts it
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
var t2 = ThreadW()
t2.start()
}
The problem is that I have to transfer value of my switcher to another Thread and don`t know how to do it correctly. I`ve tried to solve this problem through channels but my attempts have been unsuccessful. Here is the code of ThreadW file where I`ve tried to solve it via channels
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
class ThreadW() : Thread() {
override fun run() {
val a = runBlocking {
var timer : Int = 30
var t1 = ThreadS()
val channel = Channel<Boolean>()
launch { t1.start() }
val dataFromChannel = channel.receive()
println(dataFromChannel)
println("Done")
if (dataFromChannel == true)
{
Thread.sleep(100)
timer -= 1
}
println(timer)
}
}
}
Seems to me like isTrue
and timer
variables should be shared between two threads and exist outside of them. The classic problem you should be solving is possible race condition, when one thread updates the variable and the other reads the value at the same time resulting in obtaining stale value.
There are multiple ways to prevent race condition. In this case using Atomic
seems like the most simple solution.
val isTrue = AtomicBoolean(true)
val timer = AtomicInteger(30)
Atomic types guarantee that every read/write operation will happen in single instruction and there will be no interaction with the value in the middle.
fun main() {
val isTrue = AtomicBoolean(true)
val timer = AtomicInteger(30)
val threadW = thread {
while (timer.get() != 0) {
println("ThreadW runs")
if (isTrue.get()) {
println("Timer: $timer")
Thread.sleep(100L)
timer.getAndDecrement()
}
}
}
val threadS = thread {
while (timer.get() != 0) {
println("ThreadS runs")
if (isTrue.get()) {
println("isTrue = true. Sleeping...")
Thread.sleep(1000L)
isTrue.set(false)
println("Set to false")
} else {
println("isTrue = false. Sleeping...")
Thread.sleep(1000L)
isTrue.set(true)
println("Set to true")
}
}
}
}
You can also do this using coroutines, just substitute thread
with launch()
:
fun main() {
val isTrue = AtomicBoolean(true)
val timer = AtomicInteger(30)
launch(Dispatchers.Default) {
while (timer.get() != 0) {
println("ThreadW runs")
if (isTrue.get()) {
println("Timer: $timer")
delay(100L) // Note that instead of Thread.sleep() coroutines use delay()
timer.getAndDecrement()
}
}
}
launch(Dispatchers.Default) {
...
}
}
EDIT:
Usually data is not transferred from one child thread to another, it is often shared and designed thread-safely, so different threads can access and modify it.