java - definicion - setlayout jpanel
¿Cómo asegurar que `methodB` está" bloqueado "si algunos hilos están en` methodA` en Java? (5)
Class clazz
tiene dos métodos methodA()
y methodB()
.
¿Cómo asegurar que el
methodB
está "bloqueado" si algunos hilos están en elmethodA
en Java (estoy usando Java 8)?
Al "bloquear el método B", quiero decir que "espere a que no haya hilos en el método A ()". (Gracias a @AndyTurner)
Tenga en cuenta que el requisito anterior permite las siguientes situaciones:
- Múltiples hilos se encuentran simultáneamente en el
methodA
- Múltiples hilos están en el
methodB
mientras que no hay hilos en elmethodA
- Los
methodB
en elmethodB
no impiden que otros subprocesos ingresen elmethodA
Mi prueba: uso StampedLock lock = new StampedLock
.
- En el
methodA
A, llame along stamp = lock.readLock()
- Crea un nuevo método para
unlockB
y llama alock.unlockRead(stamp)
en él. - En el
methodB
, llame along stamp = lock.writeLock()
ylock.unlockWrite(stamp)
.
Sin embargo, esta estrategia de bloqueo no permite la segunda y la tercera situaciones anteriores.
Editar: me doy cuenta de que no he especificado claramente los requisitos de la sincronización entre el methodA
y el methodB
El enfoque dado por @JaroslawPawlak funciona para el requisito actual (lo acepto), pero no para mi intención original (tal vez debería aclararlo primero y luego publicarlo en otro hilo).
¿Por qué no usar un tipo de orquestador externo? Me refiero a otra clase que será responsable de llamar al método A o al método B cuando lo permita. El hilo múltiple aún se puede manipular mediante bloqueo o tal vez solo con algunos AtomicBoolean (s).
A continuación, encontrará un borrador ingenuo de cómo hacerlo.
public class MyOrchestrator {
@Autowired
private ClassWithMethods classWithMethods;
private AtomicBoolean aBoolean = = new AtomicBoolean(true);
public Object callTheDesiredMethodIfPossible(Method method, Object... params) {
if(aBoolean.compareAndSet(true, false)) {
return method.invoke(classWithMethods, params);
aBoolean.set(true);
}
if ("methodA".equals(method.getName())) {
return method.invoke(classWithMethods, params);
}
}
}
Creo que esto puede hacer el truco:
private final Lock lock = new ReentrantLock();
private final Semaphore semaphore = new Semaphore(1);
private int threadsInA = 0;
public void methodA() {
lock.lock();
threadsInA++;
semaphore.tryAcquire();
lock.unlock();
// your code
lock.lock();
threadsInA--;
if (threadsInA == 0) {
semaphore.release();
}
lock.unlock();
}
public void methodB() throws InterruptedException {
semaphore.acquire();
semaphore.release();
// your code
}
Los hilos que ingresan al methodA
aumentan el conteo e intentan obtener un permiso del semáforo (es decir, toman 1 permiso si está disponible, pero si no están disponibles, simplemente continúan sin un permiso). Cuando el último hilo abandona el methodA
, se devuelve el permiso. No podemos usar AtomicInteger
ya que cambiar el recuento y el permiso de adquisición / liberación del semáforo debe ser atómico.
Los hilos que ingresan al methodB
necesitan tener un permiso (y esperarán uno si no está disponible), pero después de que lo obtienen lo devuelven inmediatamente permitiendo que otros hilos ingresen al methodB
EDITAR:
Otra versión más simple:
private final int MAX_THREADS = 1_000;
private final Semaphore semaphore = new Semaphore(MAX_THREADS);
public void methodA() throws InterruptedException {
semaphore.acquire();
// your code
semaphore.release();
}
public void methodB() throws InterruptedException {
semaphore.acquire(MAX_THREADS);
semaphore.release(MAX_THREADS);
// your code
}
Cada hilo en el methodA
tiene un único permiso que se libera cuando el hilo abandona el methodA
Los hilos que entran en el methodB
esperan hasta que estén disponibles los 1000 permisos (es decir, no hay hilos en el methodA
), pero no los mantienen, lo que permite que otros hilos entren en ambos métodos mientras que el método methodB
todavía se está ejecutando.
En términos muy simples, lo que todos necesitan es ENTER methodB only if no thread inside methodA
.
- Simplemente puede tener un contador global, inicializado primero en 0 para registrar la cantidad de hilos que se encuentran actualmente en el
methodA()
. Debería tener unlock/mutex
asignado para proteger elcount
variables. - Los hilos que entran en los métodos A
count++
. - Los hilos que salen del método A
count--
. Los hilos que ingresan al método B primero deben verificar si el
count == 0
.methodA(){ mutex.lock(); count++; mutex.signal(); //do stuff mutex.lock(); count--; mutex.signal(); } methodB(){ mutex.lock(); if(count != 0){ mutex.signal(); return; } mutex.signal(); //do stuff }
Necesitaría un int para contar los hilos en el método A, y ReentrantLock.Condition para señalar todos los hilos esperando en el método B una vez que no haya subprocesos en el método A:
AtomicInteger threadsInMethodA = new AtomicInteger(0);
Lock threadsForMethodBLock = new ReentrantLock();
Condition signalWaitingThreadsForMethodB = threadsForMethodBLock.newCondition();
public void methodA() {
threadsInMethodA.incrementAndGet();
//do stuff
if (threadsInMethodA.decrementAndGet() == 0) {
try {
threadsForMethodBLock.lock();
signalWaitingThreadsForMethodB.signalAll();
} finally {
threadsForMethodBLock.unlock();
}
}
}
public void methodB() {
try {
threadsForMethodBLock.lock();
while (!Thread.isInterrupted() && threadsInMethodA.get() != 0) {
try {
signalWaitingThreadsForMethodB.await();
} catch (InterruptedException e) {
Thread.interrupt();
throw new RuntimeException("Not sure if you should continue doing stuff in case of interruption");
}
}
signalWaitingThreadsForMethodB.signalAll();
} finally {
threadsForMethodBLock.unlock();
}
//do stuff
}
Por lo tanto, cada subproceso que ingrese el método B primero verificará si hay nadie en el método A, y señalará los hilos de espera anteriores. Por otro lado, cada subproceso que ingrese el método A incrementará el contador para evitar que los nuevos subprocesos funcionen en el método B, y al decrementar liberará todos los subprocesos que esperan hacer cosas en el método B si no quedan subprocesos dentro del método A.
Realmente no se puede evitar que se llame a ese método o métodoB (mientras que otros subprocesos están dentro del otro método), pero puede implementar la intercomunicación de subprocesos de tal manera que aún pueda lograr lo que desea.
class MutualEx {
boolean lock = false;
public synchronized void methodA() {
if (lock) {
try {
wait();
}catch (InterruptedException e) {
}
}
//do some processing
lock = true;
notifyAll();
}
public synchronized void methodB() {
if (!lock) {
try {
wait();
}catch (InterruptedException e) {
}
}
//do some processing
lock = false;
notifyAll();
}
}
Ahora, para que esto funcione, cualquier objeto Thread creado debe tener una referencia a la misma instancia de objeto MutualEx.