JAVA同時プログラミング1_マルチスレッドの実装


JAVAでスレッドを作成する2つの方法:Threadを継承するかRunnableインタフェースを実装します.
1 Threadクラスを継承しrunメソッドを書き換える:
/**
 *            :  Thread
 *                
 * @author qhyuan1992
 *
 */
class MyThread extends Thread{
	private int count;// static
	public MyThread(String id){
		super(id);
	}
	public void run() {
		while (count < 5) {
			count ++;
			System.out.println(currentThread() + "--->" + count);
		}
	}
}
class Test{
	public static void main(String[] args) {
		Thread t1 = new MyThread("thread_1");
		Thread t2 = new MyThread("thread_2");
		t1.start();
		t2.start();
	}
}
// output(     )
//Thread[thread_1,5,main]--->1
//Thread[thread_2,5,main]--->1
//Thread[thread_1,5,main]--->2
//Thread[thread_1,5,main]--->3
//Thread[thread_1,5,main]--->4
//Thread[thread_2,5,main]--->2
//Thread[thread_1,5,main]--->5
//Thread[thread_2,5,main]--->3
//Thread[thread_2,5,main]--->4
//Thread[thread_2,5,main]--->5

2 Runnableインタフェースを実装し,runメソッドを書き換え,パラメータとしてThreadオブジェクトに渡す.
/**
 *            :  Runnable  
 *       
 * @author qhyuan1992
 */
class MyRunnable implements Runnable{
	private int count;
	public void run() {
		while (count < 5) {
			count ++;
			System.out.println(Thread.currentThread() + "--->" + count);
		}
	}
	public static void main(String[] args) {
		MyRunnable runnable = new MyRunnable();
		Thread t1 = new Thread(runnable);
		Thread t2 = new Thread(runnable);
		t1.start();
		t2.start();
	}
}
// output(     Runnable    Thread        )
//Thread[Thread-0,5,main]--->2
//Thread[Thread-1,5,main]--->2
//Thread[Thread-1,5,main]--->3
//Thread[Thread-1,5,main]--->4
//Thread[Thread-1,5,main]--->5

2つの方法の違いは何ですか?ソースコードを見ると、ThreadクラスはRunnableインタフェースを実装していることがわかります.
public
class Thread implements Runnable {
    …
    /* What will be run. */
    private Runnable target; // target                 runnable  

    /* The group of this thread */
private ThreadGroup group;
…
public Thread(Runnable target) {//               
    init(null, target, "Thread-" + nextThreadNum(), 0);
 }
}

Threadクラスのstart()メソッドを呼び出すと、ローカルメソッドstart 0()が呼び出されます.
public synchronized void start() {
……
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            ……
        }
    }
private native void start0();

要するにrunメソッドに呼び出されます.
 /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
}

Runnableを使用してThreadを構築すると、Runnableオブジェクトのrunメソッドが呼び出されます.さもないと、何もしません.しかし,継承方式でスレッドを実現すると,Threadクラスのrunメソッドは多態でまったく実行されないため,書き換えrunメソッドが実行される.
たとえば,Threadから引き継いでrunメソッドを実装し,RunnableによってThread結果を構築するとどのrunメソッドが実行されるのか.答えは確定です.Threadクラスから継承されたrunメソッドが実行されます.
class MyRunnable implements Runnable{
public void run() {
	System.out.println("MyRunnable");
	}
}

class MyThread extends Thread{
	public MyThread(Runnable r){
		super(r);
	}
	public void run() {
		System.out.println("MyThread");
	}
}

class Test{
	public static void main(String[] args) {
		Thread t = new MyThread(new MyRunnable());
		t.start();
	}
}
// output:
//MyThread

いったいThreadを使うのか、それともRunnableを使うのか.
Runnableインタフェースを実装することは、Threadクラスを継承することよりも優れています.
1.同一のプログラムコードが複数あるスレッドに適合して同一のリソースを処理し、Threadを継承する必要がある共有リソースをstaticに設定する.
2.javaでの単一継承の制限を回避
3.プログラムの堅牢性を高め、コードは複数のスレッドで共有でき、コードとデータは独立している.
最初のコードのcountフィールドをstaticに変更
class MyThread extends Thread{
	private static int count;// static
	public MyThread(String id){
		super(id);
	}
	public void run() {
		while (count < 5) {
			count ++;
			System.out.println(currentThread() + "--->" + count);
		}
	}
}
class Test{
	public static void main(String[] args) {
		Thread t1 = new MyThread("thread_1");
		Thread t2 = new MyThread("thread_2");
		t1.start();
		t2.start();
	}
}

// output (     )
//Thread[thread_1,5,main]--->2
//Thread[thread_2,5,main]--->2
//Thread[thread_2,5,main]--->3
//Thread[thread_2,5,main]--->4
//Thread[thread_2,5,main]--->5

staticを使用してもリソースを共有しても、Threadを作成したRunnableオブジェクトが同じであることを前提として、2番目の方法でマルチスレッドを実装してもよいことがわかります.
注意深く見ると、印刷の構造は私たちが予想していたものではありません.例えば、
//Thread[thread_1,5,main]--->2
//Thread[thread_2,5,main]--->2
このような結果が出たのは,マルチスレッド同時の制御不能性のためであり,スレッド同期については後述する.