2015-09-02 83 views
8

Ho il seguente tipo di codice:sincronizzazione: thread eseguono due sezioni critiche stesso ordine

synchronized block1 { 
    //only one thread in the block 
} 

{lot of code where synchronization not necessary} 

synchronized block2 { 
    //only one thread in the block. 
    //All the threads that executed block1 before this thread should have already executed this block. 
} 

Ogni thread prima esegue block1, a blocco non sincronizzato, e block2 nello stesso ordine.

Se il thread T1 esegue il blocco1 prima del thread T2, quindi T1 deve eseguire il blocco2 prima di T2. Ci sono più di due thread.

C'è un modo per ottenere questo in Java?

+0

Suppongo che non sia possibile chiamare 'block2()' all'interno di block1? –

+0

@NicholasRobinson Nessuna chiamata. Tutto il codice viene eseguito nell'ordine indicato. –

+0

È questo in un metodo? Usando 'synchronized (this) {...}' o sono metodi diversi? –

risposta

3

quanto ho capito Critical Section # 2 deve essere eseguito nello stesso ordine in cui sezione critica # 1

Se filo T1 esegue blocco1 prima discussione T2, quindi T1 deve essere avviata a blocco2 prima T2. Ci sono più di due thread.

Quindi è possibile utilizzare una coda per garantire l'ordine di esecuzione.

private Object lock = new Object(); 
private Queue<Thread> threadQueue = new ArrayDeque<>(); 

// https://stackoverflow.com/questions/32353283/synchronization-threads-execute-two-critical-sections-in-same-order 
public void executeCriticalSectionsInOrder() throws InterruptedException { 
    // Critical Section #1 
    synchronized (lock){ 
     // synchronized code #1 

     // Add self to queue 
     threadQueue.add(Thread.currentThread()); 
    } 

    // {lot of code where synchronization not necessary} 

    // Critical Section #2 
    synchronized (lock) { 
     //All the threads that executed block1 before this thread should have already executed this block. 
     // Wait turn 
     Thread t = threadQueue.element(); // Do not remove until it is self 
     while (t != Thread.currentThread()) { 
      lock.wait(); 
      // After sleep try again 
      t = threadQueue.element(); 
     } 
     // Verified own turn. Update status 
     threadQueue.remove(); 

     // synchronized code #2 

     lock.notifyAll(); // Awake any waiting thread after exiting section. 
    } 

Tuttavia Se un thread muore/uscite senza rimuovere stessa dalla coda, poi seguenti thread sarà bloccato indefinetely. Forse aggiungere un blocco finalmente per fare le pulizie?

: in Nicholas Robinson's answer è stato suggerito un ordine di posizione anziché una coda, che sembra leggermente più efficiente.

-2

Dovresti essere in grado di utilizzare uno Lock che prendi prima di chiamare block1 e rilasciarlo dopo aver chiamato block2.

static Lock lock = new ReentrantLock(); 
Random random = new Random(); 

public void block1() throws InterruptedException { 
    System.out.println("Enter block 1"); 
    Thread.sleep(random.nextInt(500)); 
    System.out.println("Leave block 1"); 
} 

public void block2() throws InterruptedException { 
    System.out.println("Enter block 2"); 
    Thread.sleep(random.nextInt(500)); 
    System.out.println("Leave block 2"); 
} 

private class BlockTester implements Runnable { 

    long start = System.currentTimeMillis(); 

    @Override 
    public void run() { 
     while (System.currentTimeMillis() < start + 10000) { 
      lock.lock(); 
      try { 
       System.out.println("Thread: " + Thread.currentThread().getName()); 
       block1(); 
       block2(); 
      } catch (InterruptedException ex) { 
       System.out.println("Interrupted"); 
      } finally { 
       lock.unlock(); 
      } 
     } 
    } 
} 

public void test() throws InterruptedException { 
    Thread[] blockTesters = { 
     new Thread(new BlockTester()), 
     new Thread(new BlockTester()), 
     new Thread(new BlockTester()), 
     new Thread(new BlockTester()), 
     new Thread(new BlockTester()) 
    }; 
    for (Thread t : blockTesters) { 
     t.start(); 
    } 
    for (Thread t : blockTesters) { 
     t.join(); 
    } 

} 
+0

Stai chiamando i metodi block1() e block2() uno dopo l'altro, non possiamo tenerli in un unico metodo? –

+0

Non vedo come questo soddisfi l'OP - VUOLE più thread per passare attraverso block1 e l'altro codice unsync - cosa non fa quello che è un thread per sorpassarne un altro e arrivare prima al blocco 2. Nello specifico vuole sospendere un thread al blocco 2 se un altro thread ha passato il blocco 1 prima che non sia ancora finito. – Elemental

+0

Come si gestisce questo: '{lotto di codice in cui la sincronizzazione non è necessaria}' –

3

Questo crea fondamentalmente una coda che le discussioni saranno aspettare fino a quando il loro numero viene in su. [AGGIORNATO]

private AtomicInteger place = new AtomicInteger(0); 
private AtomicInteger currentPlaceInQueue = new AtomicInteger(0); 
private ReentrantLock lock = new ReentrantLock(); 
private Condition notNext = lock.newCondition(); 

public void method() { 

    ThreadLocal position = new ThreadLocal(); 

    synchronized(this) { 
     //Your code 
     position.set(place.getAndIncrement()); 
    } 

    // More code 

    lock.lock(); 
    while ((int) currentPlaceInQueue.get() != position.get()) { 
     notNext.await(); 
    } 
    // More code 
    lock.unlock(); 
    currentPlaceInQueue.getAndIncrement(); 
    notNext.notifyAll(); 
} 
+0

Basta controllare, la definizione ThreadLocal potrebbe essere una variabile oggetto. –

+0

Soluzione intelligente – marthursson

+0

Non vedo il motivo per cui 'ThreadLocal' è richiesto - è limitato ad un metodo, non alla classe. –