創建Java多線程的兩種方式和線程異常

一.使用多線程的兩種方法

使用多線程的兩種方法有:繼承Thread類和實現runable接口。

二.繼承Thread類

來看一下thread類的源代碼:

class Thread implements Runnable {

首先可以看出thread類也是實現Runable接口的run方法如下:

    public void run() {        if (target != null) {
            target.run();
        }
    }

下面就是一個創建繼承Thread的類的列子:

public class ExThreadText extends Thread {    @Override
    public void run(){        for(int i=0;i<20;i++){            System.out.println("我自己創建的線程");
        }
    }

    public static void main(String[] args) {        new ExThreadText().start();        System.out.println("程序結束!");
    }
}

結果如下

首先我們需要明白在這個程序裏面有多少個線程?應該是兩個線程一個是main方法的線程一個是我run方法裏面的一個線程

從結果可以看出這兩個線程的調用和創建的順序是無關的,

在這個代碼實例裏面我們重寫了run方法,並使用 start 方法來調用,那爲什麼不用run方法來調用呢?我們來看看run方法調用會有什麼結果:

可以看出現在的兩個"線程"已經是按照順序執行的了,其實現在並不是多線程,就是一個單線程按照流程來執行。

總結:1.多線程的調用是無序的

2.多線程需要使用start方法來調用而不是run方法,同樣start方法來調用線程也是無序的

三.使用runable接口來實現多線程

由於Java不提供多繼承,所以當我們繼承了Thread類的時候我們就不能繼承其它的父類了,爲了解決這一個問題,我建議實現runable接口。

首先繼承runable接口必須重寫他的run方法,代碼如下:

public class ImRunableText implements Runnable {    @Override
    public void run(){        //重寫run方法
    }
}

那怎麼使用這個runable接口的子類呢?

我們來看看Thread類的構造方法:

可以看出Thread類的構造器可以接受實現Runable接口的子類對象(不要忘了thread類也是實現runable接口的),同時他還重載了一個構造器可以爲線程命名。

那麼我們就可以使用這個runable的實現子類了,代碼如下:

public class ImRunableText implements Runnable {    @Override
    public void run(){        //重寫run方法
        for(int i=0;i<10;i++){
            System.out.println("run方法");
        }
    }    public static void main(String[] args) {
        Thread thread =new Thread(new ImRunableText(),"線程");
        thread.start();
        System.out.println("結束了");
    }
}

結果如下:

四.線程中的數據共享和線程安全

4.1數據不共享

數據不共享那就是一個線程一個數據,單獨執行互不影響。代碼如下:

這是一個線程類,他有自己的字段num爲10。

public class DataNShare extends Thread{    private int num =10;    private String name;
    public DataNShare(String name){        this.name=name;
    }    @Override
    public  void run(){        for(;num>0;){            System.out.println("當前線程爲:"+name);            System.out.println("num值爲"+num);
            num--;
        }
    }
}

在設置一個測試類,創建三個對象,各自進行測試代碼如下:

public class Text {    public static void main(String[] args) {
        DataNShare d1=new DataNShare("線程1");
        DataNShare d2=new DataNShare("線程2");
        DataNShare d3=new DataNShare("線程3");
        d1.start();
        d2.start();
        d3.start();
    }
}

測試結果:可以看出一開始是沒有問題的,但是在後面出現了亂序的情況。

那麼出現了亂序的情況是不是就一定證明了程序出錯了呢?

我們來改進一下這個DataNShare類中的Run方法

public  void run(){        for(int i =1;num>=0;i++){
            System.out.println("當前線程爲:"+name);
            System.out.println("num值爲"+num);
            num--;            if(num==0){
                System.out.println("*************i的值爲"+i+"*************");
            }
        }
    }

那麼也就是說當我的 i 值輸出每一次輸出10那麼就是代表每一條線程都是執行互不影響的。

*3,雖然還是存在亂序的情況,但是至少保證我們的每一條線程執行都是10次沒有問題的。那麼出現亂序的情況就是輸出語句的問題。

4.2數據共享

數據共享就是多個線程可以訪問一個數據,代碼如下:

放有共享數據的線程類:

public class DataShare extends Thread {    private int num=3;//共享數據
    @Override
    public void run(){
        num--;//共享數據減一
        System.out.println("當前線程爲:"+this.currentThread().getName()+"num值爲:"+num);
    }
}

處理類:

public class Text {    public static void main(String[] args) {        //將共享數據放入3個線程裏進行處理
        DataShare d=new DataShare();
        Thread t1=new Thread(d,"t1");
        Thread t2=new Thread(d,"t2");
        Thread t3=new Thread(d,"t3");
        t1.start();
        t2.start();
        t3.start();
    }
}

結果如下:

在這裏出現的這種情況就是線程不安全狀態。那麼怎麼解決這個問題呢?

使用關鍵字synchronized(同步化的)來解決。

當一個方法使用該關鍵字那麼在多個線程執行這個方法時,每一個線程獲得執行該方法的執行權就會把這個方法上鎖結束後開鎖,只有等到這個方法沒有被上鎖時纔可以被其他線程運行。

看看改進後的代碼:

public class DataShare extends Thread {    private int num=3;//共享數據
    @Override
    synchronized public void run(){
        num--;//共享數據減一
        System.out.println("當前線程爲:"+this.currentThread().getName()+"num值爲:"+num);
    }
}

結果:

總結:當多個線程在共享一個數據時,可能會造成線程異常,應該使用關鍵字synchronized來實現同步化,在後面還會深入瞭解同步化。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章