2016-10-28 3 views
2

Ich möchte einen Test machen, zwei Thread, ein Thread ändert den Wert, ein anderer Thread eine Weile verwenden, um den ersten Thread zu warten, und dann brechen und zu beenden.Aber die Frage ist der wartende Thread läuft immer, kann nicht mehr aufhören. Eine andere Frage ist, wenn ich den Code von "System.out.println (i +" run ");", der ganze Thread kann normal arbeiten, es ist so seltsam.Thread läuft immer, kann nicht aufhören

import java.util.Date; 

public class ThreadTestTwo { 
    public int a = 0, b = 0,c = 0; 

public static void main(String[] args) { 
    System.out.println(new Date()+"start"); 
    for (int i = 0; i < 100000; i++) { 
     new ThreadTestTwo().start(i); 
     if(i % 100000 == 0){ 
      System.out.println(i/100000); 
     } 
    } 
    System.out.println(new Date()+"finish"); 
} 

public void start(final int i){ 
    Thread readThread = new Thread(){ 
     @Override 
     public void run() { 
      while (true) { 
       if(c == 1){ 
        b = a; 
//      System.out.println(i+", set b "+a); 
        break; 
       } 
//     System.out.println(i + " run"); 
      } 
     } 
    }; 
    Thread writeThread = new Thread(){ 
     @Override 
     public void run() { 
      a = 1; 
      c = 1; 
     } 
    }; 
    writeThread.setName("mywrite"); 
    readThread.setName("myread"); 
    System.out.println(i+" start"); 
    writeThread.start(); 
    readThread.start(); 

    try { 
     writeThread.join(); 
     readThread.join(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    System.out.println(i+" end"); 
    if(b != 1) 
     throw new RuntimeException("b = "+b); 
} 

} 
+0

Das Konsolenprotokoll ist: '181 Start 181 Ende 182 Start 182 Ende 183 Start 183 Ende 184 Start 184 Ende 185 Start 185 Ende 186 Start 186 Ende 187 Start 187 Ende 188 Starten' Und verwenden jstack den Faden Zustand zu sehen, dass die readThread von "if (c == 1) {" in der Zeile läuft – tongmutou

+1

Make 'a' und 'c' flüchtig oder korrekte Synchronisation verwenden, wenn Felder zwischen Threads geteilt werden. – Andreas

Antwort

1

Die schreibt von einem Thread sind nicht für einen anderen Thread gesehen werden garantiert, es sei denn die Variablen wie flüchtig gekennzeichnet sind oder auf andere Weise die Transaktionen müssen behandelt mit Synchronisation oder explizite

In Ihrem Fall eine Verriegelung, b , c sind die Instanzvariablen, auf die mehrere Threads zugreifen, und der Reader-Thread speichert die Werte im Cache und sieht daher nicht den gespülten Wert des Writer-Threads.

Bitte lesen Sie den folgenden Link für weitere Informationen: https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

-1

Es ist keine gute Idee, ein einfaches int als Signal zwischen Threads zu verwenden, da es nicht threadsicher ist.

Versuchen Sie stattdessen AtomicInteger zu verwenden oder machen Sie Ihre int volatile und sehen, was passieren wird.

+1

Das ist nur teilweise richtig. Arithmetische Operationen können nicht Thread-sicher sein, sondern liest und schreibt sind atmoic, und definieren sie als 'volatile' deaktiviert Caching-Effekte und führt ein geschieht-vor Auftrag. – Turing85

+0

Danke. Kannst du mir sagen, warum der "readThread" nicht fertig werden kann? Ich kann es nicht verstehen. – tongmutou

+0

[Allerdings gibt es Aktionen, die Sie angeben können, die atomar sind: liest und schreibt atomar sind für Referenzvariablen und für die meisten primitiven Variablen (alle Typen außer lang und doppelt).] (Https://docs.oracle.com/ Javase/tutorial/essential/concurrency/atomic.html) – Turing85

0

rate ich Ihnen mehr über Themen zu lesen. Hier ist ein interessantes Dokument von O'Really: http://chimera.labs.oreilly.com/books/1234000001805/ch09.html

Wie bei Ihrer Implementierung sollten Sie beachten, dass die Änderung einer Variablen durch einen Thread möglicherweise nicht von einem Leser-Thread gesehen wird. Um zu versuchen, entweder synchronised gets and sets zu verwenden, greifen Sie auf die Variablen in einem synchronisierten Block zu oder verwenden Sie eine AtomicReference. Sie könnten auch eine Lock wie ReantrantLock verwenden.

Auch, wenn Sie zwei Threads haben, in denen die ersten für den Eingang der zweiten warten, könnten Sie den wait() innerhalb eines synchronized Block zum ersten verwenden, so dass der zweiter des ersten notify() konnte, wenn es fertig ist seine Arbeit.

Etwas wie folgt aus:

import java.util.Date; 

public class ThreadTestTwo { 

    private int a = 0, b = 0,c = 0; 
    private final Object lock = new Object(); 
    //Any object is good as a lock, and for a simple case as this it's fine. 
    //This object will work as a monitor for the synchronized blocks. 

    public void start(final int i){ 
     Thread readThread = new Thread(){ 
      @Override 
      public void run() { 

       synchronized (lock) { 
        try { 
         while(c != 1) { 
          lock.wait(); 
         } 
        } 
        catch (InterruptedException ex) { 
         //Exception handling 
        } 

        b = a; 
       } 

       //System.out.println(i + " run"); 

      } 
     }; 

     Thread writeThread = new Thread(){ 
      @Override 
      public void run() { 

       synchronized (lock) { 
        a = 1; 
        c = 1; 
        lock.notify(); 
       } 

      } 
     }; 

     writeThread.setName("mywrite"); 
     readThread.setName("myread"); 
     System.out.println(i+" start"); 
     writeThread.start(); 
     readThread.start(); 

     System.out.println(i+" end"); 
    } 

    public static void main(String[] args) { 
     System.out.println(new Date()+"start"); 
     for (int i = 0; i < 100000; i++) { 
      new ThreadTestTwo().start(i); 
      if(i % 100000 == 0){ 
       System.out.println(i/100000); 
      } 
     } 
     System.out.println(new Date()+"finish"); 
    } 
} 

Ich würde sagen, Sie nicht join() mit dieser Methode brauchen. Wenn Sie jedoch darauf warten möchten, dass der zweite Thread gestartet wird, nachdem der erste beendet wurde, müssen Sie vor dem Starten join() verwenden. Wie folgt aus:

writeThread.start(); 
    try { 
     writeThread.join(); 
    } 
    catch (InterruptedException ex) { 
     //Exception handling 
    } 


    readThread.start(); 
    try { 
     readThread.join(); 
    } 
    catch (InterruptedException ex) { 
     //Exception handling 
    } 

Aber wenn Sie join() verwenden, für diesen speziellen Fall, würde ich sagen, Sie würden keine synchronisierten Blöcke oder Bedingungen benötigen, da der zweite Thread erst nach dem Tod des ersten beginnen würde ein. Etwas wie dieses:

public void start(final int i){ 
    Thread readThread = new Thread(){ 
     @Override 
     public void run() { 
      b = a; 
      //System.out.println(i + " run"); 

     } 
    }; 

    Thread writeThread = new Thread(){ 
     @Override 
     public void run() { 
      a = 1; 
      c = 1; 

     } 
    }; 

    writeThread.setName("mywrite"); 
    readThread.setName("myread"); 
    System.out.println(i+" start"); 


    writeThread.start(); 
    try { 
     writeThread.join(); 
    } 
    catch (InterruptedException ex) { 
     //Exception handling 
    } 


    readThread.start(); 
    try { 
     readThread.join(); 
    } 
    catch (InterruptedException ex) { 
     //Exception handling 
    } 

    System.out.println(i+" end"); 
} 

Ich hoffe, dass ich geholfen habe.

Einen schönen Tag noch.:)