The difference between synchronization and blocking
- Synchronization: Describes the behavior of waiting at the code level for the result of an IO operation until the result is returned
- Block: A state in which a thread is suspended while an IO operation is being performed, i.e. the thread is not being executed
asynchronous
- It refers to the use of multiple threads to avoid synchronous waiting
Callback hell caused by asynchrony
- A nested operation in which multiple asynchronous threads wait for a result and the next execution waits for a callback from the previous result
Is multithreading better than multithreading?
- First of all, we know the implementation principle of multithreading, in fact, the operating system level through the CPU frequently switch threads to achieve
- However, when there are too many threads, the CPU frequently switching threads itself consumes too much resources, which makes the implementation of multithreading no better than single thread
Kotlin coroutines
- Kotlin’s coroutine was born out of the multi-threaded callback hell above, and the performance problems that many threads end up with
Coroutines: A lighter “thread”
- A coroutine is a non-priority program scheduling component
- Allows subroutines to suspend and resume at specific places
- Processes contain threads
- Threads contain coroutines
- A thread can have as many coroutines as it wants as long as there is enough memory, but only one can be running at a time
- Multiple coroutines share the computer resources allocated by the thread
- Coroutines work on threads, and switching of coroutines can be controlled by the program itself, rather than scheduled by the operating system, which greatly reduces overhead
- We have no problem launching 100,000 coroutines in the coroutine field, but if you launch 100,000 threads directly, the application will go directly to OOM
Use Threads wisely
- Delay can only be used inside coroutines and is used to suspend coroutines without blocking threads
- Sleep blocks the thread
Write asynchronous code synchronously with coroutines
- Synchronous mode of the code
package com.example.kotlincore
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
class KotlinCoroutine {
private suspend fun searchOne(a): String {
delay(1000)
return "item_one"
}
private suspend fun searchTwo(a): String {
delay(1000)
return "item_two"
}
fun main(args: Array<String>) = runBlocking<Unit> {
val one = searchOne()
val two = searchTwo()
println("The items is $one and $two")}}Copy the code
- The above code is executed sequentially
- If we need to execute in parallel, we can use Async + AWIat
fun main1(a) = runBlocking<Unit> {
val one = async {
searchOne()
}
val two = async {
searchTwo()
}
val def1 = one.await()
val def2 = two.await()
}
Copy the code
- After testing, using parallel execution, time saved in half.
Shared Resource Control
- Shared resource control is to ensure the correctness of shared resources for a variable in the program or data in the database
- This is especially important in concurrent programming
The lock mode
- Locks shared resources such as Java’s synchronized
- The @synchronized method annotation in Kotlin declares a synchronized method
- Synchronized () in a code block to lock a piece of code
- Locking is not a good option in a situation of high concurrency, but in real development, you need to design the solution according to the specific scenario
- It doesn’t really make sense to go for high concurrency when we know it’s not very high
- Premature optimization is the root of all evil
- In less competitive situations, using synchronized is simpler and more semantic
- Kotlin annotates variables with @volatile in addition to @synchronized
- It can also be done using a lock, which Kotlin has wrapped into a class library
package com.example.kotlincore
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
class Sync {
private val goods = hashMapOf<Long.Int> ()@Volatile private var running = false
init {
goods[1] = 10
goods[2] = 15
}
/** ** /
@Synchronized
fun buyGoods(id: Long) {
val stock = goods.getValue(id)
if (stock > 0) {
goods[id] = stock
}
}
/** ** /
fun buyGoods2(id: Long) {
synchronized(this) {
val stock = goods.getValue(id)
goods.put(id, stock)
}
}
/** */
fun buyGood3(id: Long) {
val lock: Lock = ReentrantLock()
lock.withLock {
val stock = goods.getValue(id)
goods.put(id, stock)
}
}
}
Copy the code
Through an example to analyze the lock pattern
Question: Our code above is A performance of synchronization lock to buy goods, now have another requirement is that there are two stores, shops and B store, the goods they sell are not the same, this time, if only A synchronized lock to solve the problem is clearly wrong, because the two goods are no relationship, and this time, We need multiple locks to achieve parallelism
package com.example.kotlincore
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
/** * Initializes the different Shop classes by passing the ID through the Shop class * implements business lock separation */
class Shop(private var goods: HashMap<Long.Int>) {
private val lock: Lock = ReentrantLock()
fun buyGoods(id: Long) {
lock.withLock {
val stock = goods.getValue(id)
goods.put(id, stock - 1)}}}class ShopApi {
private val A_goods = hashMapOf<Long.Int> ()private val B_goods = hashMapOf<Long.Int> ()private var shopA: Shop
private var shopB: Shop
init {
A_goods[1] = 10
A_goods[2] = 15
B_goods[1] = 20
B_goods[2] = 10
shopA = Shop(A_goods)
shopB = Shop(B_goods)
}
fun buyGoods(shopName: String, id: Long) {
when (shopName) {
"A" -> shopA.buyGoods(id)
"B" -> shopB.buyGoods(id)
else-> {}}}}fun main(a) {
val shopApi = ShopApi()
shopApi.buyGoods("A".1)
shopApi.buyGoods("B".2)}Copy the code
In this way, we do not use the lock separation of the merchant, but there is a situation when there are more than two merchants, or tens of thousands of merchants, using the when expression to match is very useless, so what to do?
Actor: Stateful parallel computing unit
- Here is the idea: the traditional locking principle is to share memory, to achieve the security of resource sharing
- But the idea of actors is to communicate with shared memory, not with shared memory
Two principles of actors
- The message must be sent before the message is received
- The same Actor processes one message before the next
- Specific wait for time to analyze again