寫在之前
以下是《Java8編程入門官方教程》中的一些知識,如有錯誤,煩請指正。涉及的程序如需下載請移步http://down1.tupwk.com.cn/qhwkdownpage/978-7-302-38738-1.zip
多線程
多任務包括:基于進程的多任務和基于線程的多任務。進程是正在執行的程序,相當于計算機允許同時運行多個程序的功能;線程是最小的可分派代碼單元,一個程序可以一次執行多個任務,比如文本編輯器可以在打印的同時格式化文本。
單核系統中并發執行的線程共享CPU,每個線程都收到一個CPU時間片;多核系統中,多個線程可以同時執行。多線程的功能在兩個系統中都可以實現。
Thread類和Runnable接口
方法 | 含義 |
---|---|
final String getName() | 獲取線程名 |
final int getPriority() | 獲取線程優先級 |
final boolean isAlive() | 確定線程是否仍在運行 |
final void join() | 等待線程終止 |
static void sleep(long milliseconds) | 按照指定的時間掛起線程,以毫秒為單位 |
void start() | 通過調用run方法啟動線程 |
void run() | 線程的進入點 |
線程創建
兩種方法:都要用Thread類來實例化、訪問、控制線程,僅僅是創建不同。
- 實現Runnable接口
- 擴展Thread類
比較:一般僅在通過某些方式增強或者修改類的時候才擴展這些類,因此如果不重寫Thread的任何其他方法,最好事先Runnable接口。另外實現Runnable接口可以讓線程繼承除Thread類外的其他類。
Runnable接口只定義了run方法,在run方法中可以調用其他方法,使用其他類,像主線程一樣聲明變量。具體的方法通過程序來說明。
// 通過實現Runnable接口創建進程
class MyThread implements Runnable {
String thrdName;
MyThread(String name) {
thrdName = name;
}
// Entry point of thread.
public void run() {
System.out.println(thrdName + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + thrdName +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrdName + " interrupted.");
}
System.out.println(thrdName + " terminating.");
}
}
class UseThreads {
public static void main(String args[]) {
System.out.println("Main thread starting.");
// 1.創建一個可運行的線程對象
MyThread mt = new MyThread("Child #1");
// 2.在該對象上構造一個線程
Thread newThrd = new Thread(mt);
// 3.開始運行線程
newThrd.start();
for(int i=0; i<50; i++) {
System.out.print(".");
try {
Thread.sleep(100);//主線程main延遲了5秒,線程mt延遲了4秒,確保main在mt結束之后才終止
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
改進:MyThread類沒必要存儲線程名,可以在創建時就為其命名。Thread(Runnable threadob, String name)
//改進版
class MyThread implements Runnable {
Thread thrd;
// 建立一個新進程
MyThread(String name) {
thrd = new Thread(this, name);//創建時就命名
thrd.start(); // 開始進程
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count<10; count++) {
Thread.sleep(400);
System.out.println("In " + thrd.getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
}
}
class UseThreadsImproved {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread mt = new MyThread("Child #1");s//改進后創建時就啟動進程
for(int i=0; i < 50; i++) {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
另一種方法:擴展Thread,必須重寫作為新建成進入點的run方法。
class MyThread extends Thread {
// 建立一個新的線程.
MyThread(String name) {
super(name); // 線程命名
start(); // 開始線程
}
// 線程運行
public void run() {
System.out.println(getName() + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(getName() + " interrupted.");
}
System.out.println(getName() + " terminating.");
}
}
class ExtendThread {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread mt = new MyThread("Child #1");
for(int i=0; i < 50; i++) {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
創建多個線程
class MyThread implements Runnable {
Thread thrd;
// 構造線程
MyThread(String name) {
thrd = new Thread(this, name);
thrd.start(); // 開始線程
}
// 執行線程
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + thrd.getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
}
}
class MoreThreads {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread mt1 = new MyThread("Child #1");//創建并開始執行3個線程
MyThread mt2 = new MyThread("Child #2");
MyThread mt3 = new MyThread("Child #3");
for(int i=0; i < 50; i++) {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
System.out.println("Main thread ending.");
}
}
確定線程的結束
兩種方法:
final boolean isAlive()
class MoreThreads {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread mt1 = new MyThread("Child #1");
MyThread mt2 = new MyThread("Child #2");
MyThread mt3 = new MyThread("Child #3");
do {
System.out.print(".");
try {
Thread.sleep(100);
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
} while (mt1.thrd.isAlive() ||
mt2.thrd.isAlive() ||
mt3.thrd.isAlive()); //使用Alive等待所有線程終止
System.out.println("Main thread ending.");
}
}
final void join() Throws InterruptedException
等待,直到調用的線程終止
class MyThread implements Runnable {
Thread thrd;
// Construct a new thread.
MyThread(String name) {
thrd = new Thread(this, name);
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int count=0; count < 10; count++) {
Thread.sleep(400);
System.out.println("In " + thrd.getName() +
", count is " + count);
}
}
catch(InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " terminating.");
}
}
class JoinThreads {
public static void main(String args[]) {
System.out.println("Main thread starting.");
MyThread mt1 = new MyThread("Child #1");
MyThread mt2 = new MyThread("Child #2");
MyThread mt3 = new MyThread("Child #3");
try {
mt1.thrd.join();//等待,直到線程中終結,下同
System.out.println("Child #1 joined.");
mt2.thrd.join();
System.out.println("Child #2 joined.");
mt3.thrd.join();
System.out.println("Child #3 joined.");
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
System.out.println("Main thread ending.");
}
}
線程優先級
可以通過Thread的成員方法來修改線程的優先級。
final void setPriority(int level)
level的值必須在MIN_PRIORITY
和MAX_PRIORITY
的范圍內,即1-10.優先級定義為static final
變量。
可以通過Thread的成員方法獲取當前優先級設置。
final int getPriority
//線程的優先級
class Priority implements Runnable {
int count;
Thread thrd;
static boolean stop = false;
static String currentName;
/* Construct a new thread. Notice that this
constructor does not actually start the
threads running. */
Priority(String name) {
thrd = new Thread(this, name);
count = 0;
currentName = name;
}
// Begin execution of new thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
do {
count++;
if(currentName.compareTo(thrd.getName()) != 0) {
currentName = thrd.getName();
System.out.println("In " + currentName);
}
} while(stop == false && count < 10000000);
stop = true;
System.out.println("\n" + thrd.getName() +
" terminating.");
}
}
class PriorityDemo {
public static void main(String args[]) {
Priority mt1 = new Priority("High Priority");
Priority mt2 = new Priority("Low Priority");
// set the priorities
mt1.thrd.setPriority(Thread.NORM_PRIORITY+2);
mt2.thrd.setPriority(Thread.NORM_PRIORITY-2);
// start the threads
mt1.thrd.start();
mt2.thrd.start();
try {
mt1.thrd.join(); //等待線程結束
mt2.thrd.join();
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
System.out.println("\nHigh priority thread counted to " +
mt1.count);
System.out.println("Low priority thread counted to " +
mt2.count);
}
}
同步
同步:控制對象訪問的監視器,監視器通過實現“鎖”來工作。當一個對象被一個線程鎖住后,其他線程就不能訪問該對象。當該線程退出時,要為對象解鎖,使其他進程可以訪問他。有兩種同步的方法。
通過synchronized
關鍵字修改方法來同步對方法的訪問。當調用方法時,調用線程進入對象監視器,對象監視器鎖住對象。對象被鎖的同時,其他線程不能進入方法,也不能進入対象定義的其他同步方法。當線程從方法返回時,監視器為對象解鎖,允許下一個進程使用對象。
//使用同步方法控制訪問
class SumArray {
private int sum;
synchronized int sumArray(int nums[]) { //sumArray()被同步
sum = 0; // reset sum
for(int i=0; i<nums.length; i++) {
sum += nums[i];
System.out.println("Running total for " +
Thread.currentThread().getName() +
" is " + sum);
try {
Thread.sleep(10); // allow task-switch
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
return sum;
}
}
class MyThread implements Runnable {
Thread thrd;
static SumArray sa = new SumArray();
int a[];
int answer;
// Construct a new thread.
MyThread(String name, int nums[]) {
thrd = new Thread(this, name);
a = nums;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
int sum;
System.out.println(thrd.getName() + " starting.");
answer = sa.sumArray(a);
System.out.println("Sum for " + thrd.getName() +
" is " + answer);
System.out.println(thrd.getName() + " terminating.");
}
}
class Sync {
public static void main(String args[]) {
int a[] = {1, 2, 3, 4, 5};
MyThread mt1 = new MyThread("Child #1", a);
MyThread mt2 = new MyThread("Child #2", a);
try {
mt1.thrd.join();
mt2.thrd.join();
}
catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}
當上面提出的方法在無法訪問源代碼時,可以將對這種類定義的方法的調用放入synchronized
代碼塊中。
// Use a synchronized block to control access to SumArray.
class SumArray {
private int sum;
int sumArray(int nums[]) { //沒有同步!!
sum = 0; // reset sum
for(int i=0; i<nums.length; i++) {
sum += nums[i];
System.out.println("Running total for " +
Thread.currentThread().getName() +
" is " + sum);
try {
Thread.sleep(10); // allow task-switch
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
return sum;
}
}
class MyThread implements Runnable {
Thread thrd;
static SumArray sa = new SumArray();
int a[];
int answer;
// Construct a new thread.
MyThread(String name, int nums[]) {
thrd = new Thread(this, name);
a = nums;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
int sum;
System.out.println(thrd.getName() + " starting.");
// synchronize calls to sumArray()
synchronized(sa) { //對sumArray的調用被同步
answer = sa.sumArray(a);
}
System.out.println("Sum for " + thrd.getName() +
" is " + answer);
System.out.println(thrd.getName() + " terminating.");
}
}
class Sync {
public static void main(String args[]) {
int a[] = {1, 2, 3, 4, 5};
MyThread mt1 = new MyThread("Child #1", a);
MyThread mt2 = new MyThread("Child #2", a);
try {
mt1.thrd.join();
mt2.thrd.join();
} catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}
線程通信
一個線程可以通知另一個線程它被阻塞,而其他進程也可以通知它繼續執行。Java使用wait()
、notify()
、notifyAll()
支持進程間通信。三個方法是Object類實現的,是所有對象的一部分,只能在同步環境中被調用。當一個線程暫時阻塞無法運行,它調用wait()
,導致進程睡眠,對象的監視器被釋放,允許其他線程使用該對象。過一段時間后,當另一個線程進入這個監視器的時候,調用notify()
或notifyAll()
,睡眠進程被喚醒。
final void wait() throws InterruptedException//等待,直到被通知
final void wait(long millis) throws InterruptedException//或者等待一定時間后
final void wait(long millis,int nanos) throws InterruptedException
final void notify() //恢復一個等待線程
final void notifyAll() //通知所有線程
一個使用wait()和notify()
的例子
//比較難,請多次閱讀或者運行
class TickTock {
String state; // contains the state of the clock
synchronized void tick(boolean running) {
if(!running) { // stop the clock
state = "ticked";
notify(); // notify any waiting threads
return;
}
System.out.print("Tick ");
state = "ticked"; // set the current state to ticked
notify(); // let tock() run
try {
while(!state.equals("tocked"))
wait(); // wait for tock() to complete
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
synchronized void tock(boolean running) {
if(!running) { // stop the clock
state = "tocked";
notify(); // notify any waiting threads
return;
}
System.out.println("Tock");
state = "tocked"; // set the current state to tocked
notify(); // let tick() run
try {
while(!state.equals("ticked"))
wait(); // wait for tick to complete
}
catch(InterruptedException exc) {
System.out.println("Thread interrupted.");
}
}
}
class MyThread implements Runnable {
Thread thrd;
TickTock ttOb;
// Construct a new thread.
MyThread(String name, TickTock tt) {
thrd = new Thread(this, name);
ttOb = tt;
thrd.start(); // start the thread
}
// Begin execution of new thread.
public void run() {
if(thrd.getName().compareTo("Tick") == 0) {
for(int is
}
}
}
class ThreadCom {
public static void main(String args[]) {
TickTock tt = new TickTock();
MyThread mt1 = new MyThread("Tick", tt);
MyThread mt2 = new MyThread("Tock", tt);
try {
mt1.thrd.join();
mt2.thrd.join();
} catch(InterruptedException exc) {
System.out.println("Main thread interrupted.");
}
}
}
死鎖:一個線程等待另一個線程來做某事,而后者卻又在等待前者。這就像兩個過于禮貌的謙謙君子,都堅持讓對方先通過大門。
競爭:當兩個或多個線程嘗試同時訪問共享資源,但是沒有進行合適的同步時,就會發生競爭條件。
掛起、繼續執行、停止
一個標志變量用于設置掛起和繼續執行,分別是:suspend
和running
一個標志變量用于停止,設置為stop
//掛起、繼續執行、停止
class MyThread implements Runnable {
Thread thrd;
boolean suspended; //當設置為true時掛起線程
boolean stopped; //當設置為true時終止線程
MyThread(String name) {
thrd = new Thread(this, name);
suspended = false;
stopped = false;
thrd.start();
}
// This is the entry point for thread.
public void run() {
System.out.println(thrd.getName() + " starting.");
try {
for(int i = 1; i < 1000; i++) {
System.out.print(i + " ");
if((i%10)==0) {
System.out.println();
Thread.sleep(250);
}
// Use synchronized block to check suspended and stopped.
synchronized(this) {
while(suspended) {
wait();
}
if(stopped) break;
}
}
} catch (InterruptedException exc) {
System.out.println(thrd.getName() + " interrupted.");
}
System.out.println(thrd.getName() + " exiting.");
}
// Stop the thread.
synchronized void mystop() {
stopped = true;
// The following ensures that a suspended thread can be stopped.
suspended = false;
notify();
}
// Suspend the thread.
synchronized void mysuspend() {++++++++++
suspended = true;
}
// Resume the thread.
synchronized void myresume() {
suspended = false;
notify();
}
}
class Suspend {
public static void main(String args[]) {
MyThread ob1 = new MyThread("My Thread");
try {
Thread.sleep(1000); // let ob1 thread start executing
ob1.mysuspend();
System.out.println("Suspending thread.");
Thread.sleep(1000);
ob1.myresume();
System.out.println("Resuming thread.");
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Suspending thread.");
Thread.sleep(1000);
ob1.myresume();
System.out.println("Resuming thread.");
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Stopping thread.");
ob1.mystop();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
// wait for thread to finish
try {
ob1.thrd.join();
} catch (InterruptedException e) {
System.out.println("Main thread Interrupted");
}
System.out.println("Main thread exiting.");
}
}
主線程
class UseMain {
public static void main(String args[]) {
Thread thrd;
// Get the main thread.
thrd = Thread.currentThread();
// Display main thread's name.
System.out.println("Main thread is called: " +
thrd.getName());
// Display main thread's priority.
System.out.println("Priority: " +
thrd.getPriority());
System.out.println();
// Set the name and priority.
System.out.println("Setting name and priority.\n");
thrd.setName("Thread #1");
thrd.setPriority(Thread.NORM_PRIORITY+3);
System.out.println("Main thread is now called: " +
thrd.getName());
System.out.println("Priority is now: " +
thrd.getPriority());
}
}