1. 創(chuàng)建線程
Java的線程繼承自Thread的類,處理一個(gè)名為run的方法。

class MyThread extends Thread
{
private int i = 0;

public void run()
{

while (true)
{
System.out.println(++i);
if (i > 100) break;
}
}
}


public class program
{

public static void main(String[] args)
{
new MyThread().start();
}
}
而對于那些不能繼承 Thread 的類,可以采取實(shí)現(xiàn) Runnable 接口的方式進(jìn)行。
2. 執(zhí)行權(quán)轉(zhuǎn)交
對于暫時(shí)交出執(zhí)行權(quán),Java 提供了 Thread.yield() 方法.

class MyThread extends Thread
{

public void run()
{
int i = 0;

while (true)
{
System.out.println( getName()+"-----------"+(++i));
if (i > 100) break;
yield();
}
}
}


public class ThreadTest
{

public static void main(String[] args)
{
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
結(jié)果:
t1-----------1
t2-----------1
t1-----------2
t2-----------2
t1-----------3
t2-----------3
t1-----------4
t2-----------4
t1-----------5
t2-----------5
.....
Java 也提供了 Thread.sleep(); 方法,但由于 Thread.sleep 可能被 interrupt( ) 方法中斷,因此必須包含在 try{} 代碼塊中。

class MyThread extends Thread
{

public void run()
{
int i = 0;

while (true)
{
System.out.printf("%s=%d\n", getName(), ++i);
if (i > 100) break;
try

{
sleep(0);
}
catch (InterruptedException e)

{
}
}
}
}


public class program
{

public static void main(String[] args)
{
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
3. 優(yōu)先級
Java 使用 setPriority( ) 方法調(diào)整優(yōu)先級。
4. 背景線程
Java 使用 setDaemon() 方法。在 Java 中一般將背景線程稱之為"守護(hù)線程(daemon thread)"。需要注意的是即便背景線程未結(jié)束,進(jìn)程依然會終止。必須在線程啟動前設(shè)置。
5. 線程等待
Java 中都使用 join/Join() 方法阻止調(diào)用線程,直到某個(gè)線程結(jié)束。不過 Java 里面情況還是要復(fù)雜一些。當(dāng)某個(gè)線程處于 join 等待時(shí),它可能會被 interrupt( ) 方法中斷,因此也得放在 try{} 代碼塊中。
package tread;


class ThreadT extends Thread
{
public boolean stopFlag = false;


public void run()
{
int i = 0;

while (!stopFlag)
{
System.out.println(++i);
yield();
}

System.out.println("MyThread over");
}
}


class JoinThread extends Thread
{

public void run()
{
ThreadT my = new ThreadT();
my.start();


try
{
my.join();

} catch (InterruptedException e)
{
my.stopFlag = true;
System.out.println("JoinThread InterruptedException");
}
}
}


public class JoinTest
{

public static void main(String[] args)
{
JoinThread join = new JoinThread();
join.start();

try
{
join.sleep(100*5);
join.interrupt();

} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
結(jié)果:
。。。。。。
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
JoinThread InterruptedException
MyThread over
6. 資源鎖定
Java 提供了 synchronized 關(guān)鍵字用來解決多線程資源共享鎖定的問題,synchronized 可用于方法或者某個(gè)代碼段。
package tread;


class Res
{
String lock="";

synchronized static void test()
{

for (int i = 0; i < 10; i++)
{
System.out.println( Thread.currentThread().getName()+"========="+ i);
Thread.yield();
}
}


void test2()
{

synchronized(lock)
{

for (int i = 0; i < 10; i++)
{
System.out.println( Thread.currentThread().getName()+"========"+ i);
Thread.yield(); //沒有放棄鎖
}
}
}
}


class MyThreadA extends Thread
{

public void run()
{
new Res().test2();
}
}


public class SynTest
{

public static void main(String[] args)
{
MyThreadA t1 = new MyThreadA();
MyThreadA t2 = new MyThreadA();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
當(dāng)我們?nèi)∠?synchronized 關(guān)鍵字時(shí),我們會發(fā)現(xiàn) t1 和 t2 交替執(zhí)行。添加 synchronized 關(guān)鍵字以后,t2 會在 t1 執(zhí)行完成后執(zhí)行,因此對于方法的鎖定是成功的。
結(jié)果:
t1========0
。。。。
t1========7
t1========8
t1========9
t2========0
。。。
t2========8
t2========9
另外,需要注意的是 synchronized 是全局鎖定,也就是說對于靜態(tài)方法而言,它們會鎖定全部有此標(biāo)記的方法(盡管它們不是同一個(gè)方法);而對于對象實(shí)例,它們會鎖定同一對象全部有此標(biāo)記的方法(盡管它們不是同一個(gè)方法)。
7. 原子操作
所謂原子操作是指不會被線程調(diào)度機(jī)制打斷的操作;這種操作一旦開始,就一直運(yùn)行倒結(jié)束,中間不會有任何線程切換操作。
通常所說的Java原子操作包括對非long和double型的primitive進(jìn)行賦值,以及返回這兩者之外的primitive。之所以要把它們排除在外是因?yàn)樗鼈兌急容^大,而JVM的設(shè)計(jì)規(guī)范又沒有要求讀操作和賦值操作必須是原子操作(JVM可以試著去這么作,但并不保證)。不過如果你在long或double前面加了volatile,那么它就肯定是原子操作了。
看下面的例子,雖然 return i是一個(gè)原子操作,但是從獲得i的值到方法返回之間有可能被其他線程修改,因此不要主觀的認(rèn)為下面的操作是原子操作。我們還是有必要為其添加同步關(guān)鍵字synchronized。另外 ++i 和 --i 都不是原子操作,它們都涉及讀和寫兩個(gè)步驟。
《Thinking in Java》中關(guān)于最安全的做法提出了如下的方針,不過我不完全贊同。
8. 線程協(xié)同
Java用對象鎖來控制多線程安全,而所謂多線程協(xié)同無非是利用某種鎖機(jī)制讓線程暫時(shí)停頓下來,直到某個(gè)“信號”發(fā)生。為此,Java 將用來進(jìn)行多線程協(xié)同的方法放在了根 Object 上,包括 wait(); notify(); notifyAll(); 。
調(diào)用 wait() / notify() / notifyAll() 之前必須先獲取其對象鎖,否則會拋出"IllegalMonitorStateException - current thread not owner" 異常。
package tread;


class MyThread extends Thread
{

static Object o = new Object();


public void run()
{

synchronized (o)
{
int i = 0;

while (++i < 10)
{
System.out.println(i);
try

{
if (i == 5) o.wait();
}
catch (InterruptedException e)

{
}
}
}
}
}


public class SynTest2
{


public static void main(String[] args)
{
MyThread t1 = new MyThread();
t1.start();
while (t1.getState() != Thread.State.WAITING)

{
try

{
Thread.sleep(100);
}
catch (InterruptedException e)

{
}
}

System.out.println("Notify Thread
");
synchronized(MyThread.o)

{
MyThread.o.notify();
}
}
}
輸出
1
2
3
4
5
Notify Thread...
6
7
8
9
在上面這個(gè)例子中,當(dāng) i 等于 5 時(shí),我們調(diào)用了對象鎖 o 的 wait 方法阻塞線程。在 main 方法中我們循環(huán)檢查對象狀態(tài),并適時(shí)發(fā)出信號結(jié)束等待。這個(gè)例子雖然很簡單,但是由此可以處理多個(gè)線程之間的協(xié)同關(guān)系。當(dāng)然,我們還可以給 wait 方法加一個(gè)參數(shù),讓其等待特定的時(shí)間,而不是上面例子中的無限制等待。
需要注意的是 wait() 會釋放對象鎖,sleep() 則不會。接著看下面的例子。
輸出
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t1 - 5
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t2 - 5
9. 停止/中斷線程
線程退出最好自己實(shí)現(xiàn),在運(yùn)行狀態(tài)中一直檢驗(yàn)一個(gè)狀態(tài),如果這個(gè)狀態(tài)為真,就一直運(yùn)行,如果外界更改了這個(gè)狀態(tài)變量,那么線程就停止運(yùn)行。
Java 不推薦使用 stop() / about() / suspend() / resume() 之類的方法來停止線程。原因包括:
當(dāng)線程因某種原因處于阻塞等待狀態(tài)時(shí),我們就無法使用旗標(biāo)終止它,那么改用interrupt()好了。
Java的線程繼承自Thread的類,處理一個(gè)名為run的方法。

























而對于那些不能繼承 Thread 的類,可以采取實(shí)現(xiàn) Runnable 接口的方式進(jìn)行。
2. 執(zhí)行權(quán)轉(zhuǎn)交
對于暫時(shí)交出執(zhí)行權(quán),Java 提供了 Thread.yield() 方法.

































結(jié)果:











Java 也提供了 Thread.sleep(); 方法,但由于 Thread.sleep 可能被 interrupt( ) 方法中斷,因此必須包含在 try{} 代碼塊中。












































3. 優(yōu)先級
Java 使用 setPriority( ) 方法調(diào)整優(yōu)先級。
4. 背景線程
Java 使用 setDaemon() 方法。在 Java 中一般將背景線程稱之為"守護(hù)線程(daemon thread)"。需要注意的是即便背景線程未結(jié)束,進(jìn)程依然會終止。必須在線程啟動前設(shè)置。
5. 線程等待
Java 中都使用 join/Join() 方法阻止調(diào)用線程,直到某個(gè)線程結(jié)束。不過 Java 里面情況還是要復(fù)雜一些。當(dāng)某個(gè)線程處于 join 等待時(shí),它可能會被 interrupt( ) 方法中斷,因此也得放在 try{} 代碼塊中。





































































結(jié)果:













6. 資源鎖定
Java 提供了 synchronized 關(guān)鍵字用來解決多線程資源共享鎖定的問題,synchronized 可用于方法或者某個(gè)代碼段。





























































當(dāng)我們?nèi)∠?synchronized 關(guān)鍵字時(shí),我們會發(fā)現(xiàn) t1 和 t2 交替執(zhí)行。添加 synchronized 關(guān)鍵字以后,t2 會在 t1 執(zhí)行完成后執(zhí)行,因此對于方法的鎖定是成功的。
結(jié)果:









另外,需要注意的是 synchronized 是全局鎖定,也就是說對于靜態(tài)方法而言,它們會鎖定全部有此標(biāo)記的方法(盡管它們不是同一個(gè)方法);而對于對象實(shí)例,它們會鎖定同一對象全部有此標(biāo)記的方法(盡管它們不是同一個(gè)方法)。
7. 原子操作
所謂原子操作是指不會被線程調(diào)度機(jī)制打斷的操作;這種操作一旦開始,就一直運(yùn)行倒結(jié)束,中間不會有任何線程切換操作。
通常所說的Java原子操作包括對非long和double型的primitive進(jìn)行賦值,以及返回這兩者之外的primitive。之所以要把它們排除在外是因?yàn)樗鼈兌急容^大,而JVM的設(shè)計(jì)規(guī)范又沒有要求讀操作和賦值操作必須是原子操作(JVM可以試著去這么作,但并不保證)。不過如果你在long或double前面加了volatile,那么它就肯定是原子操作了。
看下面的例子,雖然 return i是一個(gè)原子操作,但是從獲得i的值到方法返回之間有可能被其他線程修改,因此不要主觀的認(rèn)為下面的操作是原子操作。我們還是有必要為其添加同步關(guān)鍵字synchronized。另外 ++i 和 --i 都不是原子操作,它們都涉及讀和寫兩個(gè)步驟。
public int getValue() { return i; }
《Thinking in Java》中關(guān)于最安全的做法提出了如下的方針,不過我不完全贊同。
- 如果你要synchronize類的一個(gè)方法,索性把所有的方法全都synchronize了。要判斷,哪個(gè)方法該synchronize,哪個(gè)方法可以不synchronize,通常是很難的,而且也沒什么把握。
- 刪除synchronized的時(shí)候要絕對小心。通常這么做是為了性能,但是synchronized的開銷在JDK1.3和1.4里已經(jīng)大為降低了。此外,只有在用profiler分析過,確認(rèn)synchronized確實(shí)是瓶頸的前提下才能這么作。
8. 線程協(xié)同
Java用對象鎖來控制多線程安全,而所謂多線程協(xié)同無非是利用某種鎖機(jī)制讓線程暫時(shí)停頓下來,直到某個(gè)“信號”發(fā)生。為此,Java 將用來進(jìn)行多線程協(xié)同的方法放在了根 Object 上,包括 wait(); notify(); notifyAll(); 。
調(diào)用 wait() / notify() / notifyAll() 之前必須先獲取其對象鎖,否則會拋出"IllegalMonitorStateException - current thread not owner" 異常。










































































輸出
1
2
3
4
5
Notify Thread...
6
7
8
9
在上面這個(gè)例子中,當(dāng) i 等于 5 時(shí),我們調(diào)用了對象鎖 o 的 wait 方法阻塞線程。在 main 方法中我們循環(huán)檢查對象狀態(tài),并適時(shí)發(fā)出信號結(jié)束等待。這個(gè)例子雖然很簡單,但是由此可以處理多個(gè)線程之間的協(xié)同關(guān)系。當(dāng)然,我們還可以給 wait 方法加一個(gè)參數(shù),讓其等待特定的時(shí)間,而不是上面例子中的無限制等待。
需要注意的是 wait() 會釋放對象鎖,sleep() 則不會。接著看下面的例子。
class MyThread extends Thread {
static Object o = new Object();
public void run() {
synchronized (o) {
int i = 0;
while (++i < 10) {
System.out.printf("%s - %d\n", Thread.currentThread().getName(), i);
try
{
if (i >= 5) o.wait();
}
catch (InterruptedException e)
{
}
}
}
}
}
public class Program {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
static Object o = new Object();
public void run() {
synchronized (o) {
int i = 0;
while (++i < 10) {
System.out.printf("%s - %d\n", Thread.currentThread().getName(), i);
try
{
if (i >= 5) o.wait();
}
catch (InterruptedException e)
{
}
}
}
}
}
public class Program {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
輸出
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t1 - 5
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t2 - 5
9. 停止/中斷線程
線程退出最好自己實(shí)現(xiàn),在運(yùn)行狀態(tài)中一直檢驗(yàn)一個(gè)狀態(tài),如果這個(gè)狀態(tài)為真,就一直運(yùn)行,如果外界更改了這個(gè)狀態(tài)變量,那么線程就停止運(yùn)行。
Java 不推薦使用 stop() / about() / suspend() / resume() 之類的方法來停止線程。原因包括:
- 在調(diào)用這些方法的時(shí)候,我們不能確定線程方法執(zhí)行到何處,是否完成了特定的邏輯。
- 這些方法會讓線程無法正確釋放對象鎖,可能造成死鎖。
- 使用旗標(biāo)(flag)。
- 調(diào)用 interrupt()。
class MyThread extends Thread {
public boolean stopFlag = false;
public void run() {
while (!stopFlag) {
try
{
System.out.println("Thread running, " + System.currentTimeMillis());
Thread.sleep(50);
}
catch (Exception e)
{
}
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
Thread.sleep(100);
}
catch (Exception e)
{
}
t.stopFlag = true;
}
}
public boolean stopFlag = false;
public void run() {
while (!stopFlag) {
try
{
System.out.println("Thread running, " + System.currentTimeMillis());
Thread.sleep(50);
}
catch (Exception e)
{
}
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
Thread.sleep(100);
}
catch (Exception e)
{
}
t.stopFlag = true;
}
}
當(dāng)線程因某種原因處于阻塞等待狀態(tài)時(shí),我們就無法使用旗標(biāo)終止它,那么改用interrupt()好了。
class MyThread extends Thread {
public void run() {
try
{
while (true) {
System.out.println("Thread running, " + System.currentTimeMillis());
synchronized (this) {
wait();
}
}
}
catch (Exception e)
{
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
while (t.getState() != Thread.State.WAITING) {
Thread.sleep(500);
}
t.interrupt();
}
catch (Exception e)
{
}
}
}
public void run() {
try
{
while (true) {
System.out.println("Thread running, " + System.currentTimeMillis());
synchronized (this) {
wait();
}
}
}
catch (Exception e)
{
}
System.out.println("Thread Stop...");
}
}
public class Program {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
try
{
while (t.getState() != Thread.State.WAITING) {
Thread.sleep(500);
}
t.interrupt();
}
catch (Exception e)
{
}
}
}