Javaの中で比較的に高級なスレッドの同期方法は総括します!
9679 ワード
1.Semaphore
1.1バイナリSemaphore
Semaphoreは比較的高度なポイントのスレッド同期ツールであり,多くの他の言語でも同様の実装がある.Semaphoreの最大の利点は、初期化時に並列数を明示的に制御できることです.このcカウンタは内部的に維持され、カウンタが0以下の場合、他のスレッドがコンカレント領域にアクセスできないため、逆に可能であるため、コンカレント数を1に設定すると、単一スレッドの同期を確保することができる.次の例では、マルチスレッド印刷をシミュレートし、各スレッドが印刷申請を提出し、印刷を実行し、最後に印刷終了を宣言します.コードは以下の通りです.
実行結果は次のとおりです.
Thread 0:タスクを印刷中 Thread 9:タスクを印刷しています Thread 8:タスクを印刷中 Thread 7:ジョブを印刷中 Thread 6:タスクを印刷中 Thread 5:タスクを印刷中 Thread 4:タスクを印刷中 Thread 3:タスクを印刷中 Thread 2:タスクを印刷中 Thread 1:タスクを印刷中 スレッド名:Thread 0スリープ:32 Thread 0:ファイルの印刷が完了しました スレッド名:Thread 9スリープ:44 Thread 9:ファイルの印刷が完了しました スレッド名:Thread 8スリープ:45 Thread 8:ファイルの印刷が完了しました スレッド名:Thread 7スリープ:65 Thread 7:ファイルの印刷が完了しました スレッド名:Thread 6スリープ:12 Thread 6:ファイルの印刷が完了しました スレッド名:Thread 5スリープ:72 Thread 5:ファイルの印刷が完了しました スレッド名:Thread 4スリープ:98 Thread 4:ファイルの印刷が完了しました スレッド名:Thread 3スリープ:58 Thread 3:ファイルの印刷が完了しました スレッド名:Thread 2スリープ:24 Thread 2:ファイルの印刷が完了しました スレッド名:Thread 1スリープ:93 Thread 1:ファイルの印刷が完了しました
すべてのスレッドが印刷申請を発行した後、同時順序で1回実行され、同時衝突はなく、信号量を先に取得した人は先に実行され、他の残りのスレッドは待機していることがわかります.この中にはもう一つの公平な信号と非公平な信号があります.基本的にjavaのすべてのマルチスレッドツールは初期化時にブール変数を指定することをサポートしています.trueは公平を示しています.つまり、待機しているすべてのスレッドがフィルタされている条件は「誰が待っているかは誰を選んで実行する」ということです.first in first outの感じがしますが、falseは不公平を示しています.(デフォルトはnon-fairnessではありません)、つまり、待機しているすべてのスレッドがフィルタリングされて実行されるのはランダムです.これは、マルチスレッドが実行順序が混乱することが多い理由です.
1.2多重同時制御
上記のコードをs=new Semaphore(3);//つまり、毎回3つのスレッドを併発できるようにすると、出力は次のようになります.
Thread 0:タスクを印刷中 Thread 9:タスクを印刷しています Thread 8:タスクを印刷中 Thread 7:ジョブを印刷中 Thread 6:タスクを印刷中 Thread 5:タスクを印刷中 Thread 3:タスクを印刷中 Thread 4:タスクを印刷中 Thread 2:タスクを印刷中 Thread 1:タスクを印刷中 スレッド名:Thread 9スリープ:26スレッド名:Thread 8スリープ:46スレッド名:Thread 0スリープ:79 Thread 9:ファイルの印刷が完了しました スレッド名:Thread 7スリープ:35 Thread 8:ファイルの印刷が完了しました スレッド名:Thread 6スリープ:90 Thread 7:ファイルの印刷が完了しました スレッド名:Thread 5スリープ:40 Thread 0:ファイルの印刷が完了しました スレッド名:Thread 3スリープ:84 Thread 5:ファイルの印刷が完了しました スレッド名:Thread 4スリープ:13 Thread 4:ファイルの印刷が完了しました スレッド名:Thread 2スリープ:77 Thread 6:ファイルの印刷が完了しました スレッド名:Thread 1スリープ:12 Thread 1:ファイルの印刷が完了しました Thread 3:ファイルの印刷が完了しました Thread 2:ファイルの印刷が完了しました
同時に競合していることは明らかです.グループ(グループごとに3つ)の同時化を実現するには、グループごとに同期します.コードの変更は次のとおりです.
ここでgetIndex()メソッドは,主に内部パケット後(同時3個をサポート)のグループ内データの同期(lockで同期)を維持するためである.
出力は次のとおりです.
Thread 0:タスクを印刷中 Thread 9:タスクを印刷しています Thread 8:タスクを印刷中 Thread 7:ジョブを印刷中 Thread 6:タスクを印刷中 Thread 5:タスクを印刷中 Thread 4:タスクを印刷中 Thread 3:タスクを印刷中 Thread 2:タスクを印刷中 Thread 1:タスクを印刷中 スレッド名:Thread 0スリープ:82 プリンタ:0番スレッド名:Thread 8スリープ:61 プリンタ:2番スレッド名:Thread 9スリープ:19 プリンタ:1 Thread 9:ファイルの印刷が完了しました スレッド名:Thread 7スリープ:82 プリンタ:1 Thread 8:ファイルの印刷が完了しました スレッド名:Thread 6スリープ:26 プリンタ:2 Thread 0:ファイルの印刷が完了しました スレッド名:Thread 5スリープ:31 プリンタ:0 Thread 6:ファイルの印刷が完了しました スレッド名:Thread 4スリープ:44 プリンタ:2 Thread 7:ファイルの印刷が完了しました スレッド名:Thread 3スリープ:54 プリンタ:1 Thread 5:ファイルの印刷が完了しました スレッド名:Thread 2スリープ:48 プリンタ:0 Thread 4:ファイルの印刷が完了しました スレッド名:Thread 1スリープ:34 プリンタ:2 Thread 3:ファイルの印刷が完了しました Thread 2:ファイルの印刷が完了しました Thread 1:ファイルの印刷が完了しました
2.CountDownLatch
CountDownLatchは、マルチタスクの同時実行をサポートするツールでもあります.主に「複数の同時イベントを待機する」ために使用されます.内部にもカウンタがあり、await()メソッドを呼び出すとスレッドは待機状態にあり、内部カウンタが0の場合にのみカウントを減らすために継続します(countDown()メソッド).すなわち、メインスレッドがすべてのサブスレッドがある条件に達するのを待って実行する必要がある場合、メインスレッドawaitのみが必要であり、各サブスレッドを起動するときにcountDown操作を行う必要がある場合、以下に会議の例をシミュレートし、全員がそろった場合にのみ会議が開始される.
出力:
会議は初期化中…!0番は到着!9人待ち!1番は到着!8人待ち!9番は到着!7人待ち!4番は到着!6人待ち!8番は到着!5人待ち!5番は到着!4人待ちメンバー!6番が到着!3人のメンバーを待つ必要がある!3番のメンバーが到着!2人のメンバーを待つ必要がある!7番のメンバーが到着!もう1人のメンバーを待つ必要がある!2番のメンバーが到着!まだ0人のメンバーを待つ必要がある!全員そろって、会議をしましょう!
3.Phaser
1.1バイナリSemaphore
Semaphoreは比較的高度なポイントのスレッド同期ツールであり,多くの他の言語でも同様の実装がある.Semaphoreの最大の利点は、初期化時に並列数を明示的に制御できることです.このcカウンタは内部的に維持され、カウンタが0以下の場合、他のスレッドがコンカレント領域にアクセスできないため、逆に可能であるため、コンカレント数を1に設定すると、単一スレッドの同期を確保することができる.次の例では、マルチスレッド印刷をシミュレートし、各スレッドが印刷申請を提出し、印刷を実行し、最後に印刷終了を宣言します.コードは以下の通りです.
import java.util.concurrent.Semaphore;
public class Program{
public static void main(String[] agrs){
PrintQueue p=new PrintQueue();
Thread[] ths=new Thread[10];
for(int i=0;i<10;i++){
ths[i]=new Thread(new Job(p),"Thread"+i);
}
for(int i=0;i<10;i++){
ths[i].start();
}
}
}
class PrintQueue{
private Semaphore s;
public PrintQueue(){
s=new Semaphore(1);//
}
public void printJob(Object document){
try{
s.acquire();
long duration=(long)(Math.random()*100);
System.out.printf(" :%s :%d",Thread.currentThread().getName(),duration);
Thread.sleep(duration);
}
catch(InterruptedException e){
e.printStackTrace();
}
finally{
s.release();
}
}
}
class Job implements Runnable{
private PrintQueue p;
public Job(PrintQueue p){
this.p=p;
}
@Override
public void run(){
System.out.printf("%s:
",Thread.currentThread().getName());
this.p.printJob(new Object());
System.out.printf("%s:
",Thread.currentThread().getName());
}
}
実行結果は次のとおりです.
Thread 0:タスクを印刷中 Thread 9:タスクを印刷しています Thread 8:タスクを印刷中 Thread 7:ジョブを印刷中 Thread 6:タスクを印刷中 Thread 5:タスクを印刷中 Thread 4:タスクを印刷中 Thread 3:タスクを印刷中 Thread 2:タスクを印刷中 Thread 1:タスクを印刷中 スレッド名:Thread 0スリープ:32 Thread 0:ファイルの印刷が完了しました スレッド名:Thread 9スリープ:44 Thread 9:ファイルの印刷が完了しました スレッド名:Thread 8スリープ:45 Thread 8:ファイルの印刷が完了しました スレッド名:Thread 7スリープ:65 Thread 7:ファイルの印刷が完了しました スレッド名:Thread 6スリープ:12 Thread 6:ファイルの印刷が完了しました スレッド名:Thread 5スリープ:72 Thread 5:ファイルの印刷が完了しました スレッド名:Thread 4スリープ:98 Thread 4:ファイルの印刷が完了しました スレッド名:Thread 3スリープ:58 Thread 3:ファイルの印刷が完了しました スレッド名:Thread 2スリープ:24 Thread 2:ファイルの印刷が完了しました スレッド名:Thread 1スリープ:93 Thread 1:ファイルの印刷が完了しました
すべてのスレッドが印刷申請を発行した後、同時順序で1回実行され、同時衝突はなく、信号量を先に取得した人は先に実行され、他の残りのスレッドは待機していることがわかります.この中にはもう一つの公平な信号と非公平な信号があります.基本的にjavaのすべてのマルチスレッドツールは初期化時にブール変数を指定することをサポートしています.trueは公平を示しています.つまり、待機しているすべてのスレッドがフィルタされている条件は「誰が待っているかは誰を選んで実行する」ということです.first in first outの感じがしますが、falseは不公平を示しています.(デフォルトはnon-fairnessではありません)、つまり、待機しているすべてのスレッドがフィルタリングされて実行されるのはランダムです.これは、マルチスレッドが実行順序が混乱することが多い理由です.
1.2多重同時制御
上記のコードをs=new Semaphore(3);//つまり、毎回3つのスレッドを併発できるようにすると、出力は次のようになります.
Thread 0:タスクを印刷中 Thread 9:タスクを印刷しています Thread 8:タスクを印刷中 Thread 7:ジョブを印刷中 Thread 6:タスクを印刷中 Thread 5:タスクを印刷中 Thread 3:タスクを印刷中 Thread 4:タスクを印刷中 Thread 2:タスクを印刷中 Thread 1:タスクを印刷中 スレッド名:Thread 9スリープ:26スレッド名:Thread 8スリープ:46スレッド名:Thread 0スリープ:79 Thread 9:ファイルの印刷が完了しました スレッド名:Thread 7スリープ:35 Thread 8:ファイルの印刷が完了しました スレッド名:Thread 6スリープ:90 Thread 7:ファイルの印刷が完了しました スレッド名:Thread 5スリープ:40 Thread 0:ファイルの印刷が完了しました スレッド名:Thread 3スリープ:84 Thread 5:ファイルの印刷が完了しました スレッド名:Thread 4スリープ:13 Thread 4:ファイルの印刷が完了しました スレッド名:Thread 2スリープ:77 Thread 6:ファイルの印刷が完了しました スレッド名:Thread 1スリープ:12 Thread 1:ファイルの印刷が完了しました Thread 3:ファイルの印刷が完了しました Thread 2:ファイルの印刷が完了しました
同時に競合していることは明らかです.グループ(グループごとに3つ)の同時化を実現するには、グループごとに同期します.コードの変更は次のとおりです.
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Program{
public static void main(String[] agrs){
PrintQueue p=new PrintQueue();
Thread[] ths=new Thread[10];
for(int i=0;i<10;i++){
ths[i]=new Thread(new Job(p),"Thread"+i);
}
for(int i=0;i<10;i++){
ths[i].start();
}
}
}
class PrintQueue{
private Semaphore s;
private boolean[] freePrinters;
private Lock lock;
public PrintQueue(){
s=new Semaphore(3);//
freePrinters=new boolean[3];
for(int i=0;i<3;i++){
freePrinters[i]=true;
}
lock=new ReentrantLock();
}
public void printJob(Object document){
try{
s.acquire();
int printerIndex=getIndex();
long duration=(long)(Math.random()*100);
System.out.printf(" :%s :%d
",Thread.currentThread().getName(),duration);
Thread.sleep(duration);
freePrinters[printerIndex]=true;// ,
}
catch(InterruptedException e){
e.printStackTrace();
}
finally{
s.release();
}
}
//
public int getIndex(){
int index=-1;
try{
lock.lock();
for(int i=0;i
ここでgetIndex()メソッドは,主に内部パケット後(同時3個をサポート)のグループ内データの同期(lockで同期)を維持するためである.
出力は次のとおりです.
Thread 0:タスクを印刷中 Thread 9:タスクを印刷しています Thread 8:タスクを印刷中 Thread 7:ジョブを印刷中 Thread 6:タスクを印刷中 Thread 5:タスクを印刷中 Thread 4:タスクを印刷中 Thread 3:タスクを印刷中 Thread 2:タスクを印刷中 Thread 1:タスクを印刷中 スレッド名:Thread 0スリープ:82 プリンタ:0番スレッド名:Thread 8スリープ:61 プリンタ:2番スレッド名:Thread 9スリープ:19 プリンタ:1 Thread 9:ファイルの印刷が完了しました スレッド名:Thread 7スリープ:82 プリンタ:1 Thread 8:ファイルの印刷が完了しました スレッド名:Thread 6スリープ:26 プリンタ:2 Thread 0:ファイルの印刷が完了しました スレッド名:Thread 5スリープ:31 プリンタ:0 Thread 6:ファイルの印刷が完了しました スレッド名:Thread 4スリープ:44 プリンタ:2 Thread 7:ファイルの印刷が完了しました スレッド名:Thread 3スリープ:54 プリンタ:1 Thread 5:ファイルの印刷が完了しました スレッド名:Thread 2スリープ:48 プリンタ:0 Thread 4:ファイルの印刷が完了しました スレッド名:Thread 1スリープ:34 プリンタ:2 Thread 3:ファイルの印刷が完了しました Thread 2:ファイルの印刷が完了しました Thread 1:ファイルの印刷が完了しました
2.CountDownLatch
CountDownLatchは、マルチタスクの同時実行をサポートするツールでもあります.主に「複数の同時イベントを待機する」ために使用されます.内部にもカウンタがあり、await()メソッドを呼び出すとスレッドは待機状態にあり、内部カウンタが0の場合にのみカウントを減らすために継続します(countDown()メソッド).すなわち、メインスレッドがすべてのサブスレッドがある条件に達するのを待って実行する必要がある場合、メインスレッドawaitのみが必要であり、各サブスレッドを起動するときにcountDown操作を行う必要がある場合、以下に会議の例をシミュレートし、全員がそろった場合にのみ会議が開始される.
import java.util.concurrent.CountDownLatch;
public class Program{
public static void main(String[] agrs){
// 10
VideoConference v=new VideoConference(10);
new Thread(v).start();
//
for(int i=0;i<10;i++){
Participant p=new Participant(i+" ",v);
new Thread(p).start();
}
}
}
class VideoConference implements Runnable{
private CountDownLatch controller;
public VideoConference(int num){
controller=new CountDownLatch(num);
}
public void arrive(String name){
System.out.printf("%s !
",name);
controller.countDown();
System.out.printf(" %d !
",controller.getCount());
}
@Override
public void run(){
try{
System.out.printf(" ...!
");
controller.await();
System.out.printf(" , !
");
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
class Participant implements Runnable{
private VideoConference conference;
private String name;
public Participant(String name,VideoConference conference){
this.name=name;
this.conference=conference;
}
@Override
public void run(){
long duration=(long)(Math.random()*100);
try{
Thread.sleep(duration);
conference.arrive(this.name);
}
catch(InterruptedException e){
}
}
}
出力:
会議は初期化中…!0番は到着!9人待ち!1番は到着!8人待ち!9番は到着!7人待ち!4番は到着!6人待ち!8番は到着!5人待ち!5番は到着!4人待ちメンバー!6番が到着!3人のメンバーを待つ必要がある!3番のメンバーが到着!2人のメンバーを待つ必要がある!7番のメンバーが到着!もう1人のメンバーを待つ必要がある!2番のメンバーが到着!まだ0人のメンバーを待つ必要がある!全員そろって、会議をしましょう!
3.Phaser
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.util.Date;
public class Program{
public static void main(String[] agrs){
Phaser phaser=new Phaser(3);
FileSearch system=new FileSearch("C:\\Windows", "log",phaser);
FileSearch apps=new FileSearch("C:\\Program Files","log",phaser);
FileSearch documents=new FileSearch("C:\\Documents And Settings","log",phaser);
Thread systemThread=new Thread(system,"System");
systemThread.start();
Thread appsThread=new Thread(apps,"Apps");
appsThread.start();
Thread documentsThread=new Thread(documents, "Documents");
documentsThread.start();
try {
systemThread.join();
appsThread.join();
documentsThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Terminated: "+ phaser.isTerminated());
}
}
class FileSearch implements Runnable{
private String initPath;
private String end;
private List results;
private Phaser phaser;
public FileSearch(String initPath,String end,Phaser phaser){
this.initPath=initPath;
this.end=end;
this.results=new ArrayList();
this.phaser=phaser;
}
private void directoryProcess(File file){
File[] files=file.listFiles();
if(files!=null){
for(int i=0;i newResults=new ArrayList();
long actualDate=new Date().getTime();
for(int i=0;i