thread - ¿Sincronizar una variable compartida entre hilos en DIFERENTES clases en java?
thread java (6)
esto es para mi ladrillo NXT.
Digamos que tengo dos clases DIFERENTES, Clase A
y Clase B
, y cada clase tiene su PROPIO hilo en ejecución.
Sin embargo, hay una instancia de variable estática llamada MotorA
que comparten ambas clases. Esta variable MotorA
es un motor FÍSICO cuyo movimiento puede ser controlado por las dos clases. Los subprocesos de Clase A
y Clase B
pueden controlar el movimiento de MotorA
, pero solo quiero que UNO de ellos controle MotorA
a la vez.
Por ejemplo, si la Clase A
está intentando rotar MotorA
hacia adelante y la Clase B
está tratando de girar MotorA
hacia atrás, solo quiero que la Clase A
MotorA
rotar MotorA
hacia adelante y bloquee el efecto de la Clase B.
Pregunta: ¿Puedo usar el MISMO bloqueo para sincronizar métodos en hilos de DIFERENTES clases?
Cada hilo se puede synchronize
en la instancia del motor mientras está trabajando con él. Sin embargo, si un hilo va a utilizar múltiples motores juntos, esto le llevará a adquirir bloqueos en cada motor. Entonces tendrías que tener mucho cuidado con el orden en que colocas los bloques sincronizados, o tendrás interbloqueos intermitentes .
En cambio, sugiero que use un solo candado para todos los motores, y nunca use ningún motor sin sostener ese candado. Como los motores son recursos estáticos, este bloqueo también podría serlo. En un programa grande, sería un poco más seguro crear su propio bloqueo privado y controlar cuidadosamente quién puede acceder a él. En este contexto, probablemente sea correcto tener una variable de bloqueo global a la que pueda acceder cualquier código.
final class Locks {
public static final Object MOTORS = new Object();
private Locks() { /* Disallow instantiation. */ }
}
Para usarlo:
final class ThreadA extends Thread {
public void run() {
...
synchronized(Locks.MOTORS) {
Motor motorA = Motors.A;
motorA.setSpeed(...);
...
}
}
}
No necesitaría limitarse a solo motores; la clave es no tener bloques anidados sincronizando en diferentes objetos. Eso puede suceder directamente en un método, o porque llama a un método con un bloque sincronizado desde un bloque sincronizado en otro método.
Esta no es una respuesta directa a la pregunta que hizo, solo algunos consejos.
Te sugiero que hagas de tu "Motor" una clase en sí misma. No tiene que dar "Control" a las otras clases de la variable del motor, ambas tienen referencias a la clase de motor. Puede sincronizar cada método en la clase de motor para que un método tenga control hasta que se complete una secuencia de comandos, luego libere el control a medida que el método retorna.
NUNCA HE visto más clases (o métodos) que hagan que el código sea más complejo. He oído que puede suceder, pero nunca lo he visto. La complejidad que he visto siempre proviene de intentar hacer cosas sin crear nuevos métodos o clases.
Los métodos en su clase de motor también pueden tener más sentido. Por ejemplo (lo siento, no tengo idea de cómo funciona esto, así que estoy adivinando) si activa el motor configurando un poco, puede exponer el método como "goForward (int speed)" y "goBack (int speed)" en lugar de "motor | = 0x10" o algo oscuro como ese.
Puede escribir métodos en Clase A
y B
que se sincronizan en el mutex llamado motor
como se muestra a continuación:
class A{
public void doSomething(){
synchronized(MotorA.motor){
//do what you want
}
}
class B{
public void doSomething(){
synchronized(MotorA.motor){
//do what you want
}
}
Aquí está la clase para MotorA
class MotorA{
public static MotorA motor=new MotorA();
......
......
......
}
EDITAR
Si ya tiene una instancia y desea compartir entre las clases A
y B
, puede pasar el objeto en constructure y sincronizar en el objeto:
class A{
final MutexObject mm;
public A(MutexObject m){
mm=m;
}
public void doSomething(){
synchronized(mm){
//do what you want
}
}
class B{
final MutexObject mm;
public B(MutexObject m){
mm=m;
}
public void doSomething(){
synchronized(mm){
//do what you want
}
}
Pero tenga cuidado al pasar el objeto de MutexObject
a los constructores de A
y B
, debería ser la misma instancia.
Sí tu puedes. En realidad puedes hacerlo así:
class MotorA {
private static MotorA motor = new MotorA();
private static final java.util.concurrent.locks.Lock lock = new java.util.concurrent.locks.ReentrantLock();
private MotorA() { }
public static MotorA getMotorA() {
return motor;
}
public static Lock getLock() {
return lock;
}
/* here go business methods for MotorA */
}
A continuación, cuando desee realizar cualquier operación en la instancia MotorA, solo necesita:
- 1) recuperar su bloqueo mediante
getLock()
- 2) Llamar a Lock.lock () en la instancia dada
- 3) adquiere MotorA singleton por
getMotorA()
- 4) realizar cualquier método en el caso de
MotorA
- 5) Llamar Lock.unlock () para liberar el bloqueo.
En este caso, el acceso al recurso estará a salvo de múltiples hilos.
O simplemente puede sincronizar en la instancia de MotorA:
class UserOfMotor1 {
public void doOperationInMotor1Thread() {
synchronized(MotorA.getMotorA()) {
MotorA motor = MotorA.getMotorA();
motor.soSth();
}
}
}
Pero en este caso, también deberá usar el bloque synchronized()
cuando thread use el recurso compartido, en su caso MotorA
. Si usa este método para controlar la sincronización, debe asegurarse de estar sincronizando en el mismo objeto en diferentes hilos; en este caso, MotorA
es Singleton
por lo que siempre obtendrá la misma instancia.
En lugar de sincronizar en la clase de usuario final (Clase A y Clase B), puede hacerlo en su propia clase de motorA. Debe ser responsabilidad de la clase MotorA sincronizar sus acciones.
Clase MotorA:
public class MotorA {
private static MotorA instance = new MotorA();
private static int pointer = 0;
private MotorA() {
}
public static MotorA getInstance() {
return instance;
}
public void rotate() {
synchronized(MotorA.class) {
System.out.println(Thread.currentThread() + " Rotating "+ pointer++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Usuario final de la clase MotorA - ClassA & ClassB que no sabe mucho sobre las existencias de los otros hilos.
public class ClassA implements Runnable {
@Override
public void run() {
doRotate();
}
private void doRotate() {
MotorA motor = MotorA.getInstance();
while (true) {
motor.rotate();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ClassB implements Runnable {
@Override
public void run() {
doRotate();
}
private void doRotate() {
MotorA motor = MotorA.getInstance();
while (true) {
motor.rotate();
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Aquí el programa principal.
public class Main {
public static void main(String[] args) {
Thread a = new Thread(new ClassA());
Thread b = new Thread(new ClassB());
Thread c = new Thread(new ClassA());
Thread d = new Thread(new ClassB());
a.start();
b.start();
c.start();
d.start();
}
}
Qué tal esto:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Shared<T> {
private T _data;
private final Lock lock = new ReentrantLock(true);
public T getData() {
try {
lock.lock();
return _data;
} finally {
lock.unlock();
}
}
public void setData(T _data) {
try {
lock.lock();
this._data = _data;
} finally {
lock.unlock();
}
}
}
Uso:
Shared<Boolean> b = new Shared<Boolean>();
b.setData(true);
//other thread
while (b.getData()) {
...
}