1. java和他的API都可以使用並發。可以指定程式包含不同的執行線程,每個線程都具有自己的方法呼叫堆疊和程式計數器,使得線程在與其他線程並發地執行能夠共用程式範圍內的資源,比如共用記憶體,這種能力被稱為多線程編程(multithreading),在核心的C和C++語言中並不具備這種能力,儘管他們影響了JAVA的設計。
2. 線程的生命週期
新線程的生命週期從“新生”狀態開始。程式啟動線程前,線程一直是“新生”狀態;程式啟動線程後,線程進入“可運行”狀態。“可運行”狀態的線程,被認為是正在執行他的任務。
在程式啟動線程之前,線程一直處於“等待”狀態,只有當另一個線程通知正在等待的線程繼續執行時,這個線程才會從“等待”狀態恢複到“可運行”狀態。
“可運行”狀態的線程可以進入“定時等待”狀態,等待一個指定的時間段。當時間到達或線程正在等待的某個事件發生時,該線程就會返回“可運行”狀態。即使處理器可以使用,處於“定時等待”狀態和“等待”狀態的線程也不能用它。當處於“可運行”狀態的線程正在等待另一個線程執行任務時,如果它提供了可選的等待時間段,則這個線程會進入“定時等待”狀態。當另一個線程通知了這個線程,或者當定時的時間段到達時(以先滿足的為準),這個線程就會返回到“可運行”狀態.。使線程進入“定時等待”狀態的另一方法是是處於“可運行”狀態的線程睡眠。睡眠線程會在“定時等待”狀態維持一個指定的時間段(稱為睡眠時間段),過了這段時間,它會返回到“可運行”狀態。當線程沒有工作要執行時,它會立即睡眠。;例
當線程試圖執行某個任務,而任務又不能立即完成,線程就從“可運行”狀態轉到“阻塞”狀態。;例。即使有處理器可供使用,“阻塞”狀態的線程也不能使用它。
線程成功完成任務,或者(由於出錯)終止了時,“可運行”線程就會進入“終止”狀態(有時稱“停滯”狀態)。
在作業系統層級,JAVA的“可運行”狀態通常包含兩個獨立的狀態。當線程首先從“新生”狀態轉到“可運行”狀態,線程處於“就緒”狀態。當作業系統將線程給處理器時,線程就從“就緒”狀態進入“運行”狀態(即開始執行),這也被稱為“調度線程”。大多數作業系統中,每個線程被賦予一小段處理器時間(時間片)來執行任務。當時間片到達時,線程就會返回到“就緒”狀態,而作業系統將另一個線程給予處理器。
3. 線程優先順序與線程調度
JAVA的線程優先順序範圍為MIN_PRIORITY(常量1)到MAX_PRIORITY(常量10),預設是NORM_PRIORITY(常量5)
4. 建立並執行線程
建立線程推介實現Runnable介面
(1)Runnable與Thread類
// Fig. 4.1: PrintTask.java// PrintTask class sleeps for a random time from 0 to 5 secondsimport java.util.Random; public class PrintTask implements Runnable { private final int sleepTime; // random sleep time for thread private final String taskName; // name of task private final static Random generator = new Random(); public PrintTask( String name ) { taskName = name; // set task name // pick random sleep time between 0 and 5 seconds sleepTime = generator.nextInt( 5000 ); // milliseconds } // end PrintTask constructor // method run contains the code that a thread will execute public void run() { try // put thread to sleep for sleepTime amount of time { System.out.printf( "%s going to sleep for %d milliseconds.\n", taskName, sleepTime ); Thread.sleep( sleepTime ); // put thread to sleep } // end try catch ( InterruptedException exception ) { System.out.printf( "%s %s\n", taskName, "terminated prematurely due to interruption" ); } // end catch // print task name System.out.printf( "%s done sleeping\n", taskName ); } // end method run} // end class PrintTask
// Fig. 4.2 ThreadCreator.java// Creating and starting three threads to execute Runnables.import java.lang.Thread; public class ThreadCreator{ public static void main( String[] args ) { System.out.println( "Creating threads" ); // create each thread with a new targeted runnable Thread thread1 = new Thread( new PrintTask( "task1" ) ); Thread thread2 = new Thread( new PrintTask( "task2" ) ); Thread thread3 = new Thread( new PrintTask( "task3" ) ); System.out.println( "Threads created, starting tasks." ); // start threads and place in runnable state thread1.start(); // invokes task1抯 run method thread2.start(); // invokes task2抯 run method thread3.start(); // invokes task3抯 run method System.out.println( "Tasks started, main ends.\n" ); } // end main} // end class RunnableTester
(2)線程管理與Executor架構
.5為顯示的建立線程,但推介使用Executor介面,用來管理Runnable對象的執行。Executor對象建立並管理一組Runnable對象的線程,這組線程就做線程池(thread pool).優點是Executor對象能複用了已經有的線程,減少為每個任務建立新線程的開銷,提高效能。
Executor介面只聲明了一個名稱為execute的方法,接收一個Runnable實參。Executor會將傳遞給他的execute方法的每個Runnable對象賦予線程池中可以用的線程。如果沒有可以用的線程,則Executor會建立一個新線程,或等待某個線程會成為可用的,並會將這個線程賦予傳遞給execute方法的Runnable對象。
ExecutorService介面擴充了Executor介面。
// Fig. 4.3: TaskExecutor.java// Using an ExecutorService to execute Runnables.import java.util.concurrent.Executors;import java.util.concurrent.ExecutorService;public class TaskExecutor{ public static void main( String[] args ) { // create and name each runnable PrintTask task1 = new PrintTask( "task1" ); PrintTask task2 = new PrintTask( "task2" ); PrintTask task3 = new PrintTask( "task3" ); System.out.println( "Starting Executor" ); // create ExecutorService to manage threads ExecutorService threadExecutor = Executors.newCachedThreadPool(); // start threads and place in runnable state threadExecutor.execute( task1 ); // start task1 threadExecutor.execute( task2 ); // start task2 threadExecutor.execute( task3 ); // start task3 // shut down worker threads when their tasks complete threadExecutor.shutdown(); System.out.println( "Tasks started, main ends.\n" ); } // end main} // end class TaskExecutor
5. 線程同步
(1)線程同步(thread synchronization),協調多個並發線程對共用資料的訪問。這種方式同步多個線程,就可以保證訪問共用對象的每個線程都能同步地將其他所有線程排除在外,這被稱為“互斥”。
另一個方法,使用JAVA內建的監控器(monitor)。每個對象都有一個監控器和監控鎖(或內建鎖)。監控器保證任何時候監控鎖由具有最大可能的唯一一個線程持有。
(2)同步的資料共用:執行原子操作。
// Adds integers to an array shared with other Runnablesimport java.lang.Runnable; public class ArrayWriter implements Runnable{ private final SimpleArray sharedSimpleArray; private final int startValue; public ArrayWriter( int value, SimpleArray array ) { startValue = value; sharedSimpleArray= array; } // end constructor public void run() { for ( int i = startValue; i < startValue + 3; i++ ) { sharedSimpleArray.add( i ); // add an element to the shared array } // end for } // end method run} // end class ArrayWrite
// Fig 5.2: SharedArrayTest.java// Executes two Runnables to add elements to a shared SimpleArray.import java.util.concurrent.Executors;import java.util.concurrent.ExecutorService;import java.util.concurrent.TimeUnit; public class SharedArrayTest{ public static void main( String[] arg ) { // construct the shared object SimpleArray sharedSimpleArray = new SimpleArray( 6 ); // create two tasks to write to the shared SimpleArray ArrayWriter writer1 = new ArrayWriter( 1, sharedSimpleArray ); ArrayWriter writer2 = new ArrayWriter( 11, sharedSimpleArray ); // execute the tasks with an ExecutorService ExecutorService executor = Executors.newCachedThreadPool(); executor.execute( writer1 ); executor.execute( writer2 ); executor.shutdown(); try { // wait 1 minute for both writers to finish executing boolean tasksEnded = executor.awaitTermination( 1, TimeUnit.MINUTES ); if ( tasksEnded ) System.out.println( sharedSimpleArray ); // print contents else System.out.println( "Timed out while waiting for tasks to finish." ); } // end try catch ( InterruptedException ex ) { System.out.println( "Interrupted while wait for tasks to finish." ); } // end catch } // end main} // end class SharedArrayTest
// Fig.5.3 : SimpleArray.java// Class that manages an integer array to be shared by multiple // threads with synchronization.import java.util.Random; public class SimpleArray{ private final int array[]; // the shared integer array private int writeIndex = 0; // index of next element to be written private final static Random generator = new Random(); // construct a SimpleArray of a given size public SimpleArray( int size ) { array = new int[ size ]; } // end constructor // add a value to the shared array public synchronized void add( int value ) { int position = writeIndex; // store the write index try { // put thread to sleep for 0-499 milliseconds Thread.sleep( generator.nextInt( 500 ) ); } // end try catch ( InterruptedException ex ) { ex.printStackTrace(); } // end catch // put value in the appropriate element array[ position ] = value; System.out.printf( "%s wrote %2d to element %d.\n", Thread.currentThread().getName(), value, position ); ++writeIndex; // increment index of element to be written next System.out.printf( "Next write index: %d\n", writeIndex ); } // end method add // used for outputting the contents of the shared integer array public String toString() { String arrayString = "\nContents of SimpleArray:\n"; for ( int i = 0; i < array.length; i++ ) arrayString += array[ i ] + " "; return arrayString; } // end method toString} // end class SimpleArray