JAvaマルチスレッド基礎まとめ

18831 ワード

JAvaマルチスレッド基礎まとめ
最近『Javaネットワークプログラミング』という本を読んでいて、スレッドの部分を読んだばかりです.本の中のこの部分の内容は多くないが、非常に精錬していて、よくまとめられている.Javaマルチスレッドのそれぞれの方法を紹介しました.以前『Javaプログラミング思想』のマルチスレッドについての説明を見たことがありますが、マルチスレッドプログラミングのテクニックと注意事項に偏っているような気がします.入門するとやはり難解で、IO流の説明も含めて同じです.この本はそれよりもっと簡単で乱暴で、入門しやすい~~~~
新しいスレッドを開始する方法
Java仮想マシンで新しいスレッドを起動するには、次の3つの方法があります.
1.Threadクラスの継承
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.bind.DatatypeConverter;
/*
 1.     ,         fileName                ,      。
 */
public class DigestThread extends Thread {

    private String fileName;

    public DigestThread(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void run() {
        try {
            FileInputStream in = new FileInputStream(fileName);
            MessageDigest sha = MessageDigest.getInstance("SHA-256");
            DigestInputStream din = new DigestInputStream(in, sha);
            while (din.read() != -1);
            din.close();
            byte[] digest = sha.digest();

            StringBuilder result = new StringBuilder(fileName);
            result.append(": ");
            result.append(DatatypeConverter.printHexBinary(digest));
            System.out.println(result);
        } catch (IOException ex) {
            System.err.println(ex);
        } catch (NoSuchAlgorithmException ex) {
            System.err.println(ex);
        }
    }

    public static void main(String[] args) {
        for (String fileName : args) {
            Thread t = new DigestThread(fileName);
            t.start();
        }
    }
}
2.Runnableインタフェースの実装
Javaの単一継承特性のため、スレッドクラスが他のクラスを継承して機能を実装する場合は、Treadクラスを継承せずにRunnableインタフェースを実装することを選択できます.
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.bind.DatatypeConverter;

/*
 1.     ,         fileName                ,      。
 */
public class DigestRunnable implements Runnable {

    private String fileName;

    public DigestRunnable(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void run() {
        try {
            FileInputStream in = new FileInputStream(fileName);
            MessageDigest sha = MessageDigest.getInstance("SHA-256");
            DigestInputStream din = new DigestInputStream(in, sha);
            while (din.read() != -1);
            din.close();
            byte[] digest = sha.digest();

            StringBuilder result = new StringBuilder(fileName);
            result.append(": ");
            result.append(DatatypeConverter.printHexBinary(digest));
            System.out.println(result);
        } catch (IOException ex) {
            System.err.println(ex);
        } catch (NoSuchAlgorithmException ex) {
            System.err.println(ex);
        }
    }

    public static void main(String[] args) {
        for (String fileName : args) {
            DigestRunnable dr = new DigestRunnable(fileName);
            Thread t = new Thread(dr);
            t.start();
        }
    }
}
3.Callableインタフェースの実現
CallableインタフェースはJava 1.5以降に導入された新しいインタフェースである.前の2つとは異なり、Callableインタフェース実行スレッドは、1つのタイプのオブジェクトを返すことができます.
import java.util.concurrent.Callable;

/*
 *     ,       data       
 */
public class FindMaxTask implements Callable<Integer> {

    private int[] data;
    private int start;
    private int end;

    FindMaxTask(int[] data, int start, int end) {
        this.data = data;
        this.start = start;
        this.end = end;
    }

    @Override
    public Integer call() throws Exception {
        // TODO Auto-generated method stub
        int max = Integer.MIN_VALUE;
        for (int i = start; i < end; i++) {
            if (data[i] > max) max = data[i];
        }
        return max;
    }

}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/*
 *     ,            ,    FindMaxTask             ,            
 */
public class MultithreadedMaxFinder {

    public static int max(int[] data) throws InterruptedException, ExecutionException {
        if (1 == data.length) {
            return data[0];
        } else if (0 == data.length) {
            throw new IllegalArgumentException();
        }

        FindMaxTask task1 = new FindMaxTask(data, 0, data.length / 2);
        FindMaxTask task2 = new FindMaxTask(data, data.length / 2, data.length);

        ExecutorService service = Executors.newFixedThreadPool(2);

        Future future1 = service.submit(task1);
        Future future2 = service.submit(task2);

        return Math.max(future1.get(), future2.get());
    }

}
スレッドからメッセージを返す
Callableインタフェーススレッド直接戻り値
Callableインタフェースを実装するスレッドは、Objectタイプで任意のタイプのオブジェクトを返すことができます(オブジェクトを取得した後、必要に応じて指定したタイプに変換したり、汎用インタフェースを使用して指定したタイプのオブジェクトを直接返すことができます).
Callableインタフェースは値を返すことができるほか,前の2つの方法とはもう1つの重要な違いがある.1.Callableインタフェースを実現するクラスはThreadクラスのstartメソッドでスレッドを起動できない.インテリジェントはExecutorServiceのsubmitメソッドでスレッドプールに提出し、有線プールでスレッドを起動する.2.futureクラスを呼び出すgetメソッドは、スレッドの戻り値を取得し、呼び出されたスレッドがまだ実行されていない場合、getメソッドは、戻り値を取得するまで現在のスレッドをブロックします.
コールバック方式でスレッド情報を得る
最初の2つの方法で起動したスレッドが情報を返す最善の方法は、コールバックメソッドを使用することです.すなわち,Java SwingにおけるListenerメカニズムと同様に,スレッド起動前にそのスレッド実行結果に興味のあるオブジェクトの参照を持たせ,スレッド実行中に結果を得た後に所有するオブジェクトのメソッドをアクティブに呼び出し,結果を伝える.サンプルコードは次のとおりです.
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.xml.bind.DatatypeConverter;

public class CallbackDigest implements Runnable {

    private String fileName;

    public CallbackDigest(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void run() {
        try {
            FileInputStream in = new FileInputStream(fileName);
            MessageDigest sha = MessageDigest.getInstance("SHA-256");
            DigestInputStream din = new DigestInputStream(in, sha);
            while (din.read() != -1);
            din.close();
            byte[] digest = sha.digest();
            //     CallbackDigestUserInterface      ,         
            CallbackDigestUserInterface.receiveDigest(digest, fileName);
        } catch (IOException ex) {
            System.err.println(ex);
        } catch (NoSuchAlgorithmException ex) {
            System.err.println(ex);
        }
    }
}
上のスレッドの計算が終了した後に呼び出されるメソッド:
import javax.xml.bind.DatatypeConverter;

public class CallbackDigestUserInterface {

    public static void receiveDigest(byte[] digest, String name) {
        StringBuilder result = new StringBuilder(name);
        result.append(": ");
        result.append(DatatypeConverter.printHexBinary(digest));
        System.out.println(result);
    }

    public static void main(String[] args) {
        for (String fileName : args) {
            CallbackDigest cb = new CallbackDigest(fileName);
            Thread t = new Thread(cb);
            t.start();
        }
    }
}
ポーリング方式によるスレッド情報の取得
このメソッドでは、スレッドクラスの関心のある情報をメンバー変数として宣言し、getメソッドを呼び出して値を取得します.ただし,戻り値が空であるか否かを常にループ検出する必要があり,推奨しない.
スレッドの同期
主にsynchronizedキーワードで実現されます.ここでは、本の原文を引用することを明確にする必要があります.
Javaは、他のスレッドが共有リソースを使用することを阻止する方法を提供していません.同じオブジェクトに同期した他のスレッドに対してこの共通リソースを使用することを防止するしかありません.
つまり、同期ロックは、コードに同期が宣言されているコードにのみ役立ちます.同期が宣言されていないコードがロックされたリソースを使用することを阻止できません.試験コード:
import java.util.Date;

public class PublicSource {

    private String str = "unlock";

    public String getStr() {
        return str;
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        PublicSource ps = new PublicSource();
        ThreadOne to = new ThreadOne(ps);
        ThreadTwo tt = new ThreadTwo(ps);
        Thread t1 = new Thread(to);
        Thread t2 = new Thread(tt);
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t2.start();
    }

}
class ThreadOne implements Runnable {

    private PublicSource ps;

    public ThreadOne(PublicSource ps) {
        this.ps = ps;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        synchronized (ps) {
            System.out.println("ThreadOne : " + ps.getStr() + " time: " + new Date());
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }
}

class ThreadTwo implements Runnable {

    private PublicSource ps;

    public ThreadTwo(PublicSource ps) {
        this.ps = ps;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        synchronized (ps) {
            System.out.println("ThreadTwo : " + ps.getStr() + " time: " + new Date());
        }

    }
}
ThreadTwoのrunメソッドのsynchronized宣言を削除した後と削除した前の実行結果を比較できます.
同期の代替方法
synchronized修飾子は、JVMのパフォーマンスに影響を与え、デッドロックが発生する可能性が高くなります.また、マルチスレッドプログラムは単一スレッドプログラムとは異なり、エラーの発生は非常に困難です.したがって、同期の問題が発生した場合は、代替案があるかどうかを考慮する必要があります.1.クラスのメンバー変数ではなくローカル変数を使用します.2.基本データ型はスレッド内で安全に変更することもできる.3.タイプを可変クラス(Stringタイプの設計と同様)に設定できます.4.スレッドセキュリティ以外のクラスをスレッドセキュリティのクラスとして使用するプライベートフィールド
デッドロック
  • デッドロックを防止するには、最も重要な技術は不要な同期を避けることである.
  • 複数のオブジェクトが同じ共有リソースセットを操作する必要がある場合、これらのリソースが同じ順序で要求されることを確認します.
  • スレッドのスケジュール
  • プリエンプトスレッドスケジューリングスレッドが一時停止するかどうかはjava仮想マシンによって決定されます.
  • コラボレーションスレッドスケジューリングjava仮想マシンは、実行中のスレッドが自分で一時停止するのを待つ.
  • すべてのJava仮想マシンは、優先順位の異なるスレッドスケジューリングを使用していることを確認します.コラボレーションスレッドスケジューリングメカニズムでは、スレッド自体が定期的に一時停止していることを確認します.スレッド自体が一時停止する方法:
  • I/Oブロック所有ロック
  • を解放しない
  • 同期オブジェクトブロックは、既に所有するロック
  • を解放しない.
  • 放棄:Thread.yield()所有しているロックを解放しない
  • スリープ:Thread.sleep()所有しているロックを解放しない
  • は別のスレッドを接続する:スレッドBがスレッドAの実行終了後に実行する場合、スレッドBでスレッドAのjoinメソッド
  • を呼び出す必要がある.
  • は、オブジェクトを待機します.オブジェクトクラスのメソッドで、既存の現在のオブジェクトのロックが解放されます.したがって、既存のロックオブジェクトに対してのみ
  • を呼び出すことができる.
  • 終了
  • は、より高い優先度スレッドによって
  • をプリエンプトする.
    スレッドプール
    この部分は说明が简単なので、他の资料を调べてから补充しましょう..
    道行はまだ浅いので、レンガを撮るのを歓迎します!!