JDK源碼學習(7)-Thread、Runnable與Callable

一、線程的基本概念

       JAVA Thread代碼中的狀態枚舉代碼爲:

public enum State {
        //初始化Thread後但是未啓動
        NEW,

        //就緒狀態的線程,但是也有可能因爲來自操作系統的某些原因造成等待,隨時等待CUP進行調用。調用了start方法,也不一定馬上就能運行。
        RUNNABLE,

      
        //阻塞狀態,運行中的線程因爲原因造成了阻塞,直到進入就緒狀態,才能被CPU重新調用。
        BLOCKED,

        //等待狀態,包括三個方法:wait()、join()、LockSupport.park()方法。
        //例如調用wait()方法後等待其他線程調用notify()與notifyAll()。
        WAITING,

        //該狀態爲一個具體時間限制的等待狀態。
        //如以下方法:sleep(timeout)、wait(timeout)、join(timeout)、LockSupport.parkNanos、LockSupport.parkUntil
        TIMED_WAITING,

        //一個線程已經完成執行或者被打斷狀態
        TERMINATED;
    }

線程生命週期示意圖:

201410311002063.png



二、java.lang.Thread

   1. 構造函數:

 public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    
 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        //name必須不能爲空。
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        // private volatile char  name[];爲name的聲明
        this.name = name.toCharArray();
        //父線程爲啓動線程
        Thread parent = currentThread();
        //打開java的安全管理器
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        //判斷是否符合規則
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

  2. run和start

 @Override
    public void run() {
        //  private Runnable target;該target可以爲runnable的run。適用於實現runnable接口的初始化線程的方法。
        if (target != null) {
            target.run();
        }
    }

  

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    
      private native void start0();

    

3.join()主要是等待該線程死亡。主要作用是該線程執行完成後纔會運行join()後面的方法。

public final void join() throws InterruptedException {
        join(0);
    }
    
 public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        //如果時間小於0,則返回異常
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
       
        if (millis == 0) {
         //如果時間爲0,則一直等待直到當前線程死亡,即isAlive爲false;每次循環等待的時間爲0
            while (isAlive()) {
                wait(0);
            }
        } else {
        //如果時間大於0,需要判斷當前時間與join方法調用時間之間的差距,如果時間差大於參數設置的時間,則不再等待循環結束直接跳出
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

例子:1.不調用join的情況

public class TestThread {
	public static void main(String[] args) throws InterruptedException {
		MyThread thread = new MyThread();
		MyThread1 thread1 = new MyThread1(thread);
		thread1.start();
	}
}

class MyThread1 extends Thread {
	MyThread myThread;

	public MyThread1(MyThread myThread) {
		this.myThread = myThread;
	}

	public void run() {
			myThread.start();
			System.out.println("end");
	}
}

class MyThread extends Thread {
	public void run() {
		int i = 0;
		while (true) {
			System.out.println(this.toString() + ":" + (i++));
			if(i==5){
				break;
			}
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

輸出結果爲:

end
Thread[Thread-0,5,main]:0
Thread[Thread-0,5,main]:1
Thread[Thread-0,5,main]:2
Thread[Thread-0,5,main]:3
Thread[Thread-0,5,main]:4

可以看到並沒有相應的順序和規律。

2.調用join()無參數的情況

修改MyThread1中的run()方法:

public void run() {
			myThread.start();
			try {
				myThread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("end");
	}

輸出結果爲:

Thread[Thread-0,5,main]:0
Thread[Thread-0,5,main]:1
Thread[Thread-0,5,main]:2
Thread[Thread-0,5,main]:3
Thread[Thread-0,5,main]:4
end

可以看到一直運行MyThread這個線程,不會運行MyThread1線程join方法下面的代碼,這是由於會一直等待MyThread線程死亡,纔會運行主線程的代碼。

3.調用join()有參數的情況

修改MyThread1中的run()方法:

public void run() {
			myThread.start();
			try {
				myThread.join(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("end");
	}

輸出結果爲:

Thread[Thread-0,5,main]:0
end
Thread[Thread-0,5,main]:1
Thread[Thread-0,5,main]:2
Thread[Thread-0,5,main]:3
Thread[Thread-0,5,main]:4

可以看到在MyThread開始後,調用join(1000)後,會等待1000毫秒後,運行MyThread1的打印代碼。而不是無限等待MyThread的死亡。

該方法的主要內容可以保證主線程不會優先於子線程先死亡,即如果主線程啓動一個子線程,可以在子線程中調用主線程的join方法,這樣就可以保證子線程死亡後主線程纔會繼續往下運行。



三、java.lang.runnable

  runnable爲接口,只有一個抽象方法run。必須實現該方法才能實現線程的調用。代碼如下:

public interface Runnable {
   public abstract void run();
}


四、java.util.concurrent.Callable與java.util.concurrent.FutureTask

  1.該類爲第三種初始化線程的方法。即實現Callable中call方法,並用FutureTask包裝Callable類,通過Thread包裝FutureTask之後調用start方法開啓線程。call方法有返回值。調用方式如下:

public class TestCallable implements Callable<String> {

	public static void main(String[] args) {
		TestCallable testCallable=new TestCallable();
		FutureTask futureTask=new FutureTask<String>(testCallable);
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+"循環變量i的值"+i);
			
			if(i==70){
				new Thread(futureTask,"有返回的線程").start();
			}
			if(i==80){
				try {
					System.out.println("子線程返回值"+futureTask.get());
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (ExecutionException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	@Override
	public String call() throws Exception {
		int i = 0;
		for (; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
		return String.valueOf(i);
	}

}

    2.java.util.concurrent.Callable的代碼爲:可見邏輯主要是通過實現call的方法,並且有返回值

public interface Callable<V> {
  V call() throws Exception;
}

    3.java.util.concurrent.FutureTask

    構造函數主要是包裝Callable類。該類實現接口interface RunnableFuture<V> extends Runnable, Future<V>,因此可以使用Thread進行包裝並調用start啓動線程

 public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    實現接口的run方法。

   public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
            //result爲callable的call()方法的返回值
                V result;
                boolean ran;
                try {
                //調用call並且將ran設置爲true
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                //將結果傳給futureTask的對象
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

五、Thread、runnable與callable的區別。

  1.runnable與callable初始化後必須作爲Thread的參數進行封裝,並調用Thread的start方法進行啓動線程。如下:

        MyThread thread = new MyThread();
		thread.start();
		MyRunable runable=new MyRunable();
		Thread runnableThread=new Thread(runable);
		runnableThread.start();

  2.runnable與callable只有一個方法,如果需要調用sleep等方法,需要調用Thread中的方法。

  

  3.建議使用runnable與callable實現線程的class,主要是因爲runnable爲接口,java中不支持多繼承,但支持多接口,如果繼承Thread來實現線程,那麼就無法再繼承別的父類了。


 4.runnable實現run方法;callable實現call方法,有返回值,有異常返回。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章