1. 創建線程

Java的線程繼承自Thread的類,處理一個名為run的方法。

class MyThread extends Thread {
  
private int i = 0;
  
public void run() {
    
while (true{
      System.out.println(
++i);
      
if (i > 100break;
    }

  }

}


public class program {
  
public static void main(String[] args){
    
new MyThread().start();
  }

}


而對于那些不能繼承 Thread 的類,可以采取實現 Runnable 接口的方式進行。
2. 執行權轉交

對于暫時交出執行權,Java 提供了 Thread.yield() 方法.
class MyThread extends Thread {  
  
public void run() {
    
int i = 0;
    
while (true{
      System.out.println( getName()
+"-----------"+(++i));
      
if (i > 100break;
      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();
  }

}

結果:

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 > 100break;
      
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. 優先級

Java 使用 setPriority( ) 方法調整優先級。
4. 背景線程

 Java 使用 setDaemon() 方法。在 Java 中一般將背景線程稱之為"守護線程(daemon thread)"。需要注意的是即便背景線程未結束,進程依然會終止。必須在線程啟動前設置。

5. 線程等待

Java 中都使用 join/Join() 方法阻止調用線程,直到某個線程結束。不過 Java 里面情況還是要復雜一些。當某個線程處于 join 等待時,它可能會被 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();
        }

        
        
        
    }

}

結果:
。。。。。。
6769
6770
6771
6772
6773
6774
6775
6776
6777
6778
JoinThread InterruptedException
MyThread over

6. 資源鎖定


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


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();
      }

    }


當我們取消 synchronized 關鍵字時,我們會發現 t1 和 t2 交替執行。添加 synchronized 關鍵字以后,t2 會在 t1 執行完成后執行,因此對于方法的鎖定是成功的。

結果:
t1========0
。。。。
t1
========7
t1
========8
t1
========9
t2
========0
。。。
t2
========8
t2
========9


另外,需要注意的是 synchronized 是全局鎖定,也就是說對于靜態方法而言,它們會鎖定全部有此標記的方法(盡管它們不是同一個方法);而對于對象實例,它們會鎖定同一對象全部有此標記的方法(盡管它們不是同一個方法)。



7. 原子操作

所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行倒結束,中間不會有任何線程切換操作。

通常所說的Java原子操作包括對非long和double型的primitive進行賦值,以及返回這兩者之外的primitive。之所以要把它們排除在外是因為它們都比較大,而JVM的設計規范又沒有要求讀操作和賦值操作必須是原子操作(JVM可以試著去這么作,但并不保證)。不過如果你在long或double前面加了volatile,那么它就肯定是原子操作了。

看下面的例子,雖然 return i是一個原子操作,但是從獲得i的值到方法返回之間有可能被其他線程修改,因此不要主觀的認為下面的操作是原子操作。我們還是有必要為其添加同步關鍵字synchronized。另外 ++i 和 --i 都不是原子操作,它們都涉及讀和寫兩個步驟。
public int getValue() { return i; }

《Thinking in Java》中關于最安全的做法提出了如下的方針,不過我不完全贊同。
  • 如果你要synchronize類的一個方法,索性把所有的方法全都synchronize了。要判斷,哪個方法該synchronize,哪個方法可以不synchronize,通常是很難的,而且也沒什么把握。
  • 刪除synchronized的時候要絕對小心。通常這么做是為了性能,但是synchronized的開銷在JDK1.3和1.4里已經大為降低了。此外,只有在用profiler分析過,確認synchronized確實是瓶頸的前提下才能這么作。
我們順便看看 C# 里面關于原子操作的一些狀況。C# 通過 Interlocked 類來提供原子操作的支持,稱之為互鎖操作。“互鎖操作是原子的 — 即整個操作是不能由相同變量上的另一個互鎖操作所中斷的單元。這在搶先多線程操作系統中是很重要的,在這樣的操作系統中,線程可以在從某個內存地址加載值之后但是在有機會更改和存儲該值之前被掛起。 ”


8. 線程協同

Java用對象鎖來控制多線程安全,而所謂多線程協同無非是利用某種鎖機制讓線程暫時停頓下來,直到某個“信號”發生。為此,Java 將用來進行多線程協同的方法放在了根 Object 上,包括 wait(); notify(); notifyAll(); 。

調用 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 (++< 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

在上面這個例子中,當 i 等于 5 時,我們調用了對象鎖 o 的 wait 方法阻塞線程。在 main 方法中我們循環檢查對象狀態,并適時發出信號結束等待。這個例子雖然很簡單,但是由此可以處理多個線程之間的協同關系。當然,我們還可以給 wait 方法加一個參數,讓其等待特定的時間,而不是上面例子中的無限制等待。

需要注意的是 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();
  }
}

輸出
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t1 - 5
t2 - 1
t2 - 2
t2 - 3
t2 - 4
t2 - 5

9. 停止/中斷線程

 線程退出最好自己實現,在運行狀態中一直檢驗一個狀態,如果這個狀態為真,就一直運行,如果外界更改了這個狀態變量,那么線程就停止運行。

 Java 不推薦使用 stop() / about() / suspend() / resume() 之類的方法來停止線程。原因包括:
  • 在調用這些方法的時候,我們不能確定線程方法執行到何處,是否完成了特定的邏輯。
  • 這些方法會讓線程無法正確釋放對象鎖,可能造成死鎖。
推薦的方法:
  • 使用旗標(flag)。
  • 調用 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;
  }
}

當線程因某種原因處于阻塞等待狀態時,我們就無法使用旗標終止它,那么改用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)
    {
    }
  }
}