Use sincronizado() y withLock() para bloquear recursos compartidos en Kotlin
- Generar un Proyecto Kotlin
- Interferencia de subprocesos en acción
-
Bloquear recursos compartidos usando la función
synchronized()
en Kotlin -
Bloquear recursos compartidos usando la función
withLock()
en Kotlin - Conclusión
Cuando se trabaja con aplicaciones de subprocesos múltiples, debemos asegurarnos de que el acceso a los recursos compartidos se administre para evitar datos inconsistentes. La sincronización es el enfoque que solemos usar cuando queremos controlar el acceso a los recursos compartidos en nuestras aplicaciones.
La sincronización se realiza mediante bloqueos, lo que garantiza que ningún subproceso acceda al recurso compartido hasta que el subproceso que se está ejecutando actualmente haya terminado de ejecutarse. Un subproceso primero debe obtener el bloqueo de un objeto antes de acceder al recurso compartido y liberarlo una vez hecho.
Hay diferentes formas de crear bloqueos y, en este tutorial, aprenderemos cómo se crean los bloqueos en Kotlin para controlar el acceso a los recursos compartidos.
Generar un Proyecto Kotlin
Abra el entorno de desarrollo IntelliJ y seleccione Archivo > Nuevo > Proyecto
. En la ventana que se abre, ingrese el nombre del proyecto como kotlin-withlock
, seleccione Kotlin
en la sección Idioma y seleccione Intellij
como Sistema de compilación. Finalmente, presione el botón Crear
para generar el proyecto.
Una vez que se haya generado la aplicación, cree un archivo llamado Main.kt
en la carpeta src/main/kotlin
. Usaremos este archivo para implementar todos los ejemplos de código para este tutorial.
Interferencia de subprocesos en acción
La interferencia de subprocesos ocurre en una aplicación multiproceso sin control sobre cómo se accede a los recursos compartidos. El código siguiente muestra cómo se produce la interferencia de subprocesos en una aplicación de subprocesos múltiples.
Copie y pegue el siguiente código en el archivo Main.kt
.
class Calculator {
fun addIntArray() {
var initialValue = 0;
for (number in 1..5) {
println(
Thread.currentThread().name
+ " ---> " + initialValue + " + "
+ number + " = "
.plus(String.format("%d", (initialValue + number)))
);
initialValue += number;
Thread.sleep(500);
}
}
}
fun main() {
val calculator = Calculator();
Thread {
calculator.addIntArray();
}.start();
Thread {
calculator.addIntArray();
}.start();
}
En este código, hemos creado un método llamado addIntArray()
dentro de una clase llamada Calculator
. Este método es un recurso compartido en nuestro código, y dado que crearemos varios subprocesos, hemos agregado un método println()
que nos permite registrar el subproceso que se está ejecutando actualmente en la consola.
Cuando se invoca el método sleep()
, el subproceso que se está ejecutando actualmente se suspende durante 500 milisegundos y otro subproceso comienza a ejecutarse. Esto significa que los subprocesos acceden a nuestro método simultáneamente, lo que puede provocar inconsistencias en los datos.
En el método main()
, creamos dos hilos y llamamos al método start()
en ellos para comenzar a ejecutar el método addIntArray()
del objeto calculadora que hemos creado.
Ejecute este código y observe cómo se intercalan los subprocesos, como se muestra a continuación.
Thread-0 ---> 0 + 1 = 1
Thread-1 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-0 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 10 + 5 = 15
Bloquear recursos compartidos usando la función synchronized()
en Kotlin
Este ejemplo utiliza el método addIntArray()
que hemos implementado en la sección anterior para mostrar cómo se gestiona el acceso a él. Comente el método main()
creado en el ejemplo anterior y copie y pegue el siguiente código en el archivo Main.kt
después del comentario.
val calculator = Calculator();
fun sumUsingSynchronization(): Unit = synchronized(calculator) {
calculator.addIntArray();
}
fun main() {
Thread {
sumUsingSynchronization();
}.start();
Thread {
sumUsingSynchronization()
}.start();
}
El objetivo principal de este tutorial es aprender cómo se crean bloqueos para garantizar el acceso exclusivo de hilos a un recurso compartido. Por defecto, los objetos tienen bloqueos de monitor implícitos que los subprocesos usan para lograr la sincronización, y este código usa un bloqueo de monitor implícito pasando el objeto Calculadora
a la función sincronizada()
.
La función synchronized()
es invocada por la función sumUsingSynchronization()
que a su vez invoca el método addIntArray()
. En el método main()
, creamos dos hilos e invocamos el método start()
en ellos para iniciar la ejecución.
Tenga en cuenta que dado que nuestro código usa un bloqueo de monitor implícito, no hay interferencia entre los dos subprocesos. El primer subproceso utiliza el método addIntArray()
exclusivamente hasta que haya terminado de ejecutarse antes de que comience el segundo subproceso.
La función synchronized()
no solo se limita a los bloqueos implícitos del monitor, sino que también puede usar cualquier otro bloqueo, como ReentrantLock, y solo necesitamos pasar el bloqueo como argumento de este método. Ejecute este código y asegúrese de que el resultado sea como se muestra a continuación.
Thread-0 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-0 ---> 3 + 3 = 6
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 0 + 1 = 1
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-1 ---> 10 + 5 = 15
Bloquear recursos compartidos usando la función withLock()
en Kotlin
Comente el método main()
creado en el ejemplo anterior y copie y pegue el siguiente código en el archivo Main.kt
después del comentario.
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
val reentrantLock: Lock = ReentrantLock();
fun sumUsingLock(): Unit = reentrantLock.withLock {
calculator.addIntArray();
}
fun main() {
Thread {
sumUsingLock();
}.start();
Thread {
sumUsingLock()
}.start();
}
En este código, hemos creado un método llamado sumUsingLock()
que se asigna a la función de extensión withLock()
de la interfaz Lock
. La función de extensión withLock()
utiliza la implementación de bloqueo proporcionada para ejecutar el bloque de código exclusivamente para cada uno de los múltiples subprocesos creados.
En este ejemplo, creamos un bloqueo personalizado utilizando ReentrantLock
, que implementa la interfaz Lock
. Este bloqueo es utilizado por los hilos creados para ejecutar el método addIntArray()
exclusivamente.
Ejecute este código y asegúrese de que el resultado sea como se muestra a continuación.
Thread-0 ---> 0 + 1 = 1
Thread-0 ---> 1 + 2 = 3
Thread-0 ---> 3 + 3 = 6
Thread-0 ---> 6 + 4 = 10
Thread-0 ---> 10 + 5 = 15
Thread-1 ---> 0 + 1 = 1
Thread-1 ---> 1 + 2 = 3
Thread-1 ---> 3 + 3 = 6
Thread-1 ---> 6 + 4 = 10
Thread-1 ---> 10 + 5 = 15
Conclusión
En este tutorial, hemos aprendido cómo administrar el acceso a los recursos compartidos al cubrir cómo se crean bloqueos en estos recursos. Cubrimos cómo ocurre la interferencia, cómo se usan los bloqueos con la función synchronized()
y cómo se usan los bloqueos con la función withLock()
.
Tenga en cuenta que la diferencia entre estos dos enfoques es que la función synchronized()
no está limitada a ninguna implementación de bloqueo, pero la función withLock()
está limitada a la implementación Lock
.
David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.
LinkedIn GitHub