Java併發編程--1.Thread和Runnable

建立線程

Java有兩種方式建立線程, 繼承Thread類和實現Runnable接口ide

繼承Thread

步驟:this

1.自定義一個類繼承Thread類, 重寫run方法

2.建立自定義類的對象,調用start()

例如:spa

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("in thread");
    }
}

MyThread thread = new MyThread();
thread.start();

實現Runnable

步驟:線程

1. 自定義一個類,實現Runnable,重寫run()

2.建立一個Thread對象, 構造方法的參數是自定義類的對象, 調用start()

例如:code

class MyRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("in Runable");
        
    }
}

MyRunnable runnable = new MyRunnable();
new Thread(runnable).start();

 Thread和Runnable的區別

買火車票的案例

買5張火車票,咱們但願多個線程總共買5張票, 下面是兩種實現的代碼對象

繼承Thread: blog

class MyThread extends Thread {
    private int ticket = 5; 
    
    @Override
    public void run() {
        for (int i=0;i<10;i++) {  
            if(ticket > 0){  
                System.out.println("ticket = " + ticket--);  
            }  
        }  
    }
}

new MyThread().start();
new MyThread().start();

咱們new了2個線程對象,分別獨立的執行2個對象中的代碼繼承

控制檯輸出: 忽略輸出順序,能夠看出2個線程分別賣了5張接口

ticket = 5
ticket = 4
ticket = 3
ticket = 5
ticket = 2
ticket = 4
ticket = 3
ticket = 2
ticket = 1
ticket = 1

實現Runnable接口:資源

class MyRunnable implements Runnable {
    private int ticket = 5; 
    
    @Override
    public void run() {
        for (int i=0;i<10;i++) {  
            if(ticket > 0){  
                System.out.println("ticket = " + ticket--);  
            }  
        } 
    }
}

MyRunnable r = new MyRunnable();
new Thread(r).start(); 
new Thread(r).start(); 

 兩個Thread對象共享一個Runnable對象

控制檯輸出: 能夠看出2個線程共買了5張, 達到了資源共享的目的

ticket = 5
ticket = 4
ticket = 3
ticket = 2
ticket = 1

Runnable的優點

經過上面的案例, 能夠總結出:

1.數據可以被多個線程共享,實現了代碼與數據是獨立的
2.適合多個相同程序代碼的線程區處理同一資源的狀況

 改變線程狀態

線程中斷: interrupt 

線程的中斷是一種協做機制,線程中斷後並不必定當即中斷,而是要求線程在合適的時間中斷本身,每一個線程都有一個boolean的中斷標誌,該屬性再也不Thread中

interrupt() : 只是設置線程的中斷標誌

public static void main(String[] args)  {
        
        Runnable r = new Runnable() {
            @Override
            public void run() {
                
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    //若是去掉這句, 下面的輸出語句就會答應
                    return;
                }
                
                System.out.println("中斷");
                
            }
        };
        
        Thread t = new Thread(r);
        t.start();
        
        //主線程休眠,確保剛纔啓動的線程執行一段時間
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        //中斷線程
        t.interrupt();

在主線程中啓動新線程, 主線程休眠2秒鐘, 新線程休眠5秒鐘

2秒後主線程會中斷新線程,新線程的休眠狀態被打斷, 拋出 InterruptedException

程序進入catch塊中,執行return語句, 從run()返回,而後線程消亡

interrupted() :  線程中斷返回true, 並清除線程狀態

isInterrupted():  線程中斷返回true, 不能改變線程的狀態

public static void main(String[] args)  {

    System.out.println(Thread.interrupted());
        
    //中斷
    Thread.currentThread().interrupt();
    System.out.println(Thread.interrupted());
        
    System.out.println(Thread.interrupted());
}    

//控制檯輸出: false   true   false

也就是說, interrupted()會改變中斷狀態

線程掛起 : Joining 

join() : 在A線程中,調用B線程對象的該方法, 那麼A線程等B線程執行完後再執行

  public static int a = 0;  
      
    public static void main(String[] args) throws Exception {  
        Runnable r = new Runnable(){
            @Override
            public void run() {
                 for (int k = 0; k < 5; k++) {  
                        a = a + 1;  
                    }
                 System.out.println("a" + a);
            }
        };
        
        Thread t = new Thread(r);  
        t.start();   
        t.join();
        
        for (int i=0; i<1; i++) {                
            System.out.println("i=" + i);  
        }  
    } 

控制檯輸出: a=5  i=0; 

若是把 t.join()該行去掉, 則輸出 i=0  a=5 ; 由於主線程首先得到時間片執行, 而後在執行其它線程

線程間通訊: wait-notify 實現生產者 - 消費者模型

經過Object類的wait(), notify(), notifyAll()能夠實現線程間的通訊

wait() : 將當前線程置入休眠狀態,直到接到通知或被中斷爲止
notify() : 若是有多個線程等待,則線程規劃器任意挑選出其中一個wait()狀態的線程來發出通知
nofityAll() : 使全部原來在該對象上wait的線程通通退出wait的狀態

生產者和消費者在同一時間段共享同一存儲空間, 生產者向空間內生產數據,消費者取出數據

 

下面是個例子:

public class ProductConsumer{
    public static void main(String[] args) {
        Shared s = new Shared();
        new Producer(s).start();
        new Consumer(s).start();
    }
}

/** 負責存儲數據 */
class Shared {
    private char c;
    private volatile boolean writeable = true;
    
    synchronized void setSharedChar(char c) {
        while (!writeable)
        try  {
            wait();
        }
        catch (InterruptedException ie){
        }
        
        this.c = c;
        System.out.println(c + " produced by producer.");
        
        writeable = false;
        notify();
    }
    
    synchronized char getSharedChar(){
        while (writeable)
        try{
            wait();
        }
        catch (InterruptedException ie){
        }
        
        writeable = true;
        notify();
        
        System.out.println(c + " consumed by consumer.");
        return c;
    }
}
    
/** 生產者 */
class Producer extends Thread{
    private final Shared s;
    
    Producer(Shared s){
        this.s = s;
    }
    
    @Override
    public void run() {
        for (char ch = 'A'; ch <= 'Z'; ch++){
            synchronized (s) {
                s.setSharedChar(ch);
            }
        }
    }
}
    
/** 消費者 */
class Consumer extends Thread {
    private final Shared s;

    Consumer(Shared s) {
        this.s = s;
    }

    @Override
    public void run() {
        char ch;
        do {
            synchronized (s) {
                ch = s.getSharedChar();
            }
            
        } 
        while (ch != 'Z');
    }
}

控制檯輸出:

A produced by producer.
A consumed by consumer.

...................

線程狀態總結

如今咱們能建立線程, 並能改變線程的狀態,下圖是對線程狀態的總結

相關文章
相關標籤/搜索