java實現多線程的兩種方式及售票實例


1、繼承Thread類實現多線程

繼承Thread類的方法盡管被我列為一種多線程實現方式,但Thread本質上也是實現了Runnable接口的一個實例,它代表一個線程的實例,並且,啟動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啟動一個新線程,並執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend Thread,並復寫run()方法,就可以啟動新線程並執行自己定義的run()方法。例如:

package com.multithread.learning;

class Thread1 extends Thread{
    private String name;
    public Thread1(String name) {
       this.name=name;
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + "運行  :  " + i);
            try {
                sleep((int) Math.random() * 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       
    }
}
public class Main {

    public static void main(String[] args) {
        Thread1 mTh1=new Thread1("A");
        Thread1 mTh2=new Thread1("B");
        mTh1.start();
        mTh2.start();

    }

}

運行結果:

A運行:0
B運行:0
A運行:1
B運行:1
A運行:2
B運行:2
A運行:3
B運行:3
A運行:4
B運行:4

說明:
程序啟動運行main時候,java虛擬機啟動一個進程,主線程main在main()調用時候被創建。隨着調用MitiSay的兩個對象的start方法,另外兩個線程也啟動了,這樣,整個應用就在多線程下運行。
 
注意:start()方法的調用后並不是立即執行多線程代碼,而是使得該線程變為可運行態(Runnable),什么時候運行是由操作系統決定的。
從程序運行的結果可以發現,多線程程序是亂序執行。因此,只有亂序執行的代碼才有必要設計為多線程。
Thread.sleep()方法調用目的是不讓當前線程獨自霸占該進程所獲取的CPU資源,以留出一定時間給其他線程執行的機會。
實際上所有的多線程代碼執行順序都是不確定的,每次執行的結果都是隨機的。

但是start方法重復調用的話,會出現java.lang.IllegalThreadStateException異常。

二、實現java.lang.Runnable接口

采用Runnable也是非常常見的一種,我們只需要重寫run方法即可。下面也來看個實例。

package com.thread;

/**
 * Created by HJS on 2017/8/11.
 */
class Thread2 implements Runnable {
    private String name;
    public Thread2(String name){
        this.name=name;
    }
    @Override
    public void run(){
        for (int i=0;i<5;i++){
            System.out.println(name+"運行:"+i);
            try{
                Thread.sleep((int)Math.random()*10);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

}

 

package com.thread;

/**
 * Created by HJS on 2017/8/11.
 */
public class Main2 {
        public static void main(String[] args){
            new Thread(new Thread2("C")).start();
            new Thread(new Thread2("D")).start();
        }
    }

結果1:

D運行:0
C運行:0
D運行:1
C運行:1
D運行:2
C運行:2
D運行:3
C運行:3
C運行:4
D運行:4

結果2:

D運行:0
C運行:0
D運行:1
D運行:2
C運行:1
C運行:2
C運行:3
C運行:4
D運行:3
D運行:4

說明:
        Thread2類通過實現Runnable接口,使得該類有了多線程類的特征。run()方法是多線程程序的一個約定。所有的多線程代碼都在run方法里面。Thread類實際上也是實現了Runnable接口的類。
在啟動的多線程的時候,需要先通過Thread類的構造方法Thread(Runnable target) 構造出對象,然后調用Thread對象的start()方法來運行多線程代碼。
實際上所有的多線程代碼都是通過運行Thread的start()方法來運行的。因此,不管是擴展Thread類還是實現Runnable接口來實現多線程,最終還是通過Thread的對象的API來控制線程的,熟悉Thread類的API是進行多線程編程的基礎。
 

三、Thread和Runnable的區別

如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable接口的話,則很容易的實現資源共享。

 

總結:

實現Runnable接口比繼承Thread類所具有的優勢:

1):適合多個相同的程序代碼的線程去處理同一個資源

2):可以避免java中的單繼承的限制

3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立

4):線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類

 

 

提醒:main方法其實也是一個線程。在java中所以的線程都是同時啟動的,至於什么時候,哪個先執行,完全看誰先得到CPU的資源。

在java中,每次程序運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用java命令執行一個類的時候,實際上都會啟動一個JVM,每一個JVM實際就是在操作系統中啟動了一個進程。

 

四、多線程售票

     

public class TestTicket {

    public static void main(String[] args) {

        Runnable st = new SellTicket(new Tick());
        new Thread(st, "A").start();
        new Thread(st, "B").start();
        new Thread(st, "C").start();
        new Thread(st, "D").start();
    }

    public static class SellTicket implements Runnable {

        public Tick tick;
        
        Object mutex = new Object();

        public SellTicket(Tick tick) {
            this.tick = tick;
        }

        public void run() {
                
                while (tick.getCount() > 0) {
                    synchronized(mutex) { //需要有一個鎖變量
                        if(tick.getCount() <=0) break; //synchronized之前沒鎖住其他線程(有可能進入到while等待,當進入后需要重新判斷count值是大於0,不然就會變成0或負數)
                        int temp = tick.getCount(); 
                        System.out.println(Thread.currentThread().getName()
                                + "-----sale" + temp--);
                        
                         tick.setCount(temp); 
                    }
                }
            
        }
    }

    public static class Tick {
        private int count = 10;

        private Tick() {
        }

        private static final class lazyhodler {
            public static final Tick INSTANCE = new Tick();
        }

        public static final Tick getInstance() {
            return lazyhodler.INSTANCE;
        }

        public int getCount() {
            return count;
        }

        public void setCount(int count) {
            this.count = count;
        }

    }
}

 運行結果:

A-----sale10
A-----sale9
B-----sale8
C-----sale7
C-----sale6
C-----sale5
C-----sale4
C-----sale3
C-----sale2
C-----sale1

 這里使用到synchronized關鍵字來添加內置鎖,具體關於synchronized的學習請移步下一篇文章:http://www.cnblogs.com/jiansen/p/7351872.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM