亚洲欧美日韩综合系列在线_91精品人妻一区二区_欧美大肥婆一级特大AA片_九色91视频免费观看_亚洲综合国产精品_av中文字幕在线不卡_久久精品色综合网_看黄色视频的软件_无卡无码高清中文字幕码2024_亚洲欧美日韩天堂网

第一章 Java多線程技能

來源:jiansin 發(fā)布時間:2018-08-29 17:02:34 閱讀量:1021

進程和多線程的概念及其優(yōu)點

進程是操作系統(tǒng)結(jié)構(gòu)的基礎(chǔ),是一次程序的執(zhí)行;是一個程序及其數(shù)據(jù)在處理機上順序執(zhí)行時所發(fā)生的活動;是程序在一個數(shù)據(jù)集合上運行的過程,它是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位。進程是受操作系統(tǒng)管理的基本運行單元。

線程可以理解成是在進程中獨立運行的子任務(wù)。比如QQ.exe運行時就有很多的子任務(wù)在同時運行。再如,好友視頻線程、下載文件線程、傳輸數(shù)據(jù)線程、發(fā)送表情線程等,這些不同的任務(wù)或者說功能都可以同時運行,其中每一項任務(wù)完全可以理解成是“線程”在工作,傳文件、聽音樂、發(fā)送圖片表情等功能都有對應(yīng)的線程在后臺默默地運行。

多線程的優(yōu)點:可以最大限度的利用CPU的空閑時間來處理其他的任務(wù),而CPU在這些任務(wù)之間不停的切換,由于切換的速度非???,給使用者的感受就是這些任務(wù)似乎在同時運行。所以使用多線程技術(shù)后,可以在同一時間內(nèi)運行更多不同種類的任務(wù)。

單任務(wù)的特點就是排隊執(zhí)行,也就是同步,就像在cmd中輸入一條命令后,必須等待這條命令執(zhí)行完才可以執(zhí)行下一條命令一樣。這就是單任務(wù)環(huán)境的缺點,即CPU利用率大幅降低。而多線程情況下,CPU完全可以在任務(wù)1和任務(wù)2之間來回切換,使任務(wù)2不必等到10秒再運行,系統(tǒng)的運行效率大大得到提升,這就是多線程技術(shù)的優(yōu)點,使用多線程也就是在使用異步,千萬不要把開發(fā)環(huán)境里代碼的順序當(dāng)成線程執(zhí)行的順序,線程被調(diào)用的時機是隨機的。

使用多線程

繼承Thread 類

在Java 的JDK 開發(fā)包中,已經(jīng)自帶了對多線程技術(shù)的支持,可以很方便地進行多線程編
程。實現(xiàn)多線程編程的方式主要有兩種,一種是繼承Thread 類,另一種是實現(xiàn)Runnable 接口。

package com.mythread.www;public class MyThread extends Thread {    @Override
    public void run() {            super.run();
            System.out.println("MyThread");
        }
}
package test;import com.mythread.www.MyThread;public class Run {public static void main(String[] args) {
        MyThread mythread = new MyThread();
        mythread.start();
        System.out.println(" 運行結(jié)束! ");
    }
}

輸出結(jié)果

運行結(jié)束!
MyThread

MyThread.java 類中的run 方法執(zhí)行的時間比較晚,這也說
明在使用多線程技術(shù)時,代碼的運行結(jié)果與代碼執(zhí)行順序或調(diào)用順序是無關(guān)的。

實現(xiàn)Runnable 接口

如果欲創(chuàng)建的線程類已經(jīng)有一個父類了,這時就不能再繼承自Thread 類了,因為Java
不支持多繼承,所以就需要實現(xiàn)Runnable 接口來應(yīng)對這樣的情況。

package myrunnable;public class MyRunnable implements Runnable {    @Override
    public void run() {
        System.out.println(" 運行中!");
    }    public static void main(String[] args) {
        Runnable runnable=new MyRunnable();
        Thread thread=new Thread(runnable);
        thread.start();
        System.out.println(" 運行結(jié)束!");
    }
}

輸出結(jié)果

運行結(jié)束!
運行中!

那也就意味著構(gòu)造函數(shù)Thread(Runnable target) 不光可以傳入Runnable 接口的對象,還
可以傳入一個Thread 類的對象,這樣做完全可以將一個Thread 對象中的run() 方法交由其他
的線程進行調(diào)用。

實例變量與線程安全

自定義線程類中的實例變量針對其他線程可以有共享與不共享之分,這在多個線程之間
進行交互時是很重要的一個技術(shù)點。

不共享數(shù)據(jù)的情況

public class MyThread extends Thread {    private int count = 5;    public MyThread(String name) {    super();    this.setName(name);// 設(shè)置線程名稱
    }    @Override
    public void run() {        super.run();        while (count > 0) {
            count--;
            System.out.println(" 由 " + this.currentThread().getName()
            + " 計算,count=" + count);
        }
    }
}
public class Run {    public static void main(String[] args) {
        MyThread a=new MyThread("A");
        MyThread b=new MyThread("B");
        MyThread c=new MyThread("C");
        a.start();
        b.start();
        c.start();
    }
}

一共創(chuàng)建了3 個線程,每個線程都有各自的count 變量,自己減少
自己的count 變量的值。這樣的情況就是變量不共享,此示例并不存在多個線程訪問同一個實例變量的情況。

共享數(shù)據(jù)的情況就是多個線程可以訪問同一個變量,比如在實現(xiàn)投票功能的軟件時,多個線程可以同時處理同一個人的票數(shù)。

public class MyThread extends Thread {     private int count=5;    @Override
    public void run() {        super.run();
        count--;
        System.out.println(" 由 "+Thread.currentThread().getName()+" 計算, count="+count);
    }
}public class Run {    public static void main(String[] args) {
        MyThread mythread=new MyThread();
        Thread a=new Thread(mythread,"A");
        Thread b=new Thread(mythread,"B");
        Thread c=new Thread(mythread,"C");
        Thread d=new Thread(mythread,"D");
        Thread e=new Thread(mythread,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }
}

輸出結(jié)果

 由 B 計算, count=3
 由 A 計算, count=3
 由 C 計算, count=2
 由 D 計算, count=1
 由 E 計算, count=0

線程A 和B 打印出的count 值都是
3,說明A 和B 同時對count 進行處理,產(chǎn)生了“非線程安全”問
題。
在某些JVM 中,i-- 的操作要分成如下3 步:

1 取得原有i 值。
2 計算i-1。
3 對i 進行賦值。
在這3 個步驟中,如果有多個線程同時訪問,那么一定會出現(xiàn)非線程安全問題。

在這里占不解決同步問題,讀者可以自行思考。

留意i-- 與System.out.println() 的異常

本節(jié)將通過程序案例細化一下println() 方法與i++ 聯(lián)合使用時“有可能”出現(xiàn)的另外一種異常情況,并說明其中的原因。

package extthread;public class MyThread extends Thread {    private int i = 5;    @Override
    public void run() {
        System.out.println("i=" + (i--) + " threadName="
        + Thread.currentThread().getName());        // 注意:代碼i-- 由前面項目中單獨一行運行改成在當(dāng)前項目中在println() 方法中直接進行打印
    }
}
package test;import extthread.MyThread;public class Run {    public static void main(String[] args) {
        MyThread run = new MyThread();
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);
        Thread t3 = new Thread(run);
        Thread t4 = new Thread(run);
        Thread t5 = new Thread(run);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

輸出結(jié)果

i=5 threadName=Bi=4 threadName=Ci=5 threadName=Ai=2 threadName=Di=3 threadName=E

雖然println() 方法在內(nèi)部是同步的,但i-- 的操作卻是在進入
println() 之前發(fā)生的,所以有發(fā)生非線程安全問題的概率。

 /**
     * Prints a String and then terminate the line.  This method behaves as
     * though it invokes <code>{@link #print(String)}</code> and then
     * <code>{@link #println()}</code>.
     *
     * @param x  The <code>String</code> to be printed.
     */
    public void println(String x) {        synchronized (this) {
            print(x);
            newLine();
        }
    }

currentThread()

currentThread() 方法可返回代碼段正在被哪個線程調(diào)用的信息。

/**
 * Created by Lee on 2018/7/25.
 */public class MyThread extends  Thread {    public MyThread () {
        System.out.println("構(gòu)造方法打印的是:" + Thread.currentThread().getName());
    }

    @Override    public void run() {
        System.out.println("run方法打印的是" + Thread.currentThread().getName());
    }    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();        //thread.start();
    }
}

輸出結(jié)果

構(gòu)造方法打印的是:main
run方法打印的是Thread-0//run方法打印的是main

再來一個比較復(fù)雜的

package mythread;public class CountOperate extends Thread {    public CountOperate() {
        System.out.println("CountOperate---begin");
        System.out.println("Thread.currentThread().getName()="
        + Thread.currentThread().getName());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("CountOperate---end");
    }
    @Override    public void run() {
        System.out.println("run---begin");
        System.out.println("Thread.currentThread().getName()="
        + Thread.currentThread().getName());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("run---end");
    }    public static void main(String[] args) {
        CountOperate c = new CountOperate();
        Thread t1 = new Thread(c);
        t1.setName("A");
        t1.start();
    }
}

輸出結(jié)果

CountOperate---beginThread.currentThread().getName()=main
this.getName()=Thread-0CountOperate---endrun---beginThread.currentThread().getName()=A
this.getName()=Thread-0run---end

因為Thread.currentThread()是當(dāng)前代碼段正在那個線程調(diào)用,CountOperate的構(gòu)造函數(shù)是有main主線程調(diào)用的,run是由Thread線程調(diào)用。同時線程的默認名稱是Thread-(No),這點可以有Thread類的構(gòu)造函數(shù)看出。其中一個構(gòu)造函數(shù)如下:

 public Thread(Runnable target) {
     init(null, target, "Thread-" + nextThreadNum(), 0);
} /* For autonumbering anonymous threads. */
 private static int threadInitNumber; private static synchronized int nextThreadNum() {     return threadInitNumber++;
}

重點也就是最后一個this.getName() 為什么是Thread-0?
由上面的Thread構(gòu)造函數(shù)可以看出當(dāng)使用一個Thread對象作為參數(shù)去實例化一個Thread對象時,實現(xiàn)Thread的線程類被緩存進了target對象,而當(dāng)調(diào)用run()方法時,Thread類是這樣實現(xiàn)的

/**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {        if (target != null) {
            target.run();
        }
    }

 isAlive() 方法

方法isAlive() 的功能是判斷當(dāng)前的線程是否處于活動狀態(tài)。

/**
 * @program: Demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo  extends Thread{    @Override
    public void run() {
        System.out.println("run=" + this.isAlive());
    }    public static void main(String[] args) {
        Demo mythread = new Demo();
        System.out.println("begin ==" + mythread.isAlive());
        mythread.start();
        System.out.println("end ==" + mythread.isAlive());
    }

}

輸出結(jié)果

begin ==falseend ==truerun=true

方法isAlive() 的作用是測試線程是否處于活動狀態(tài)。什么是活動狀態(tài)呢?活動狀態(tài)就是線程已經(jīng)啟動且尚未終止。線程處于正在運行或準備開始運行的狀態(tài),就認為線程是“存活”的。

需要說明一下,如以下代碼:

System.out.println("end ==" + mythread.isAlive());
雖然在上面的示例中打印的值是true,但此值是不確定的。打印true 值是因為mythread線程還未執(zhí)行完畢,所以輸出true。如果代碼更改如下:

public static void main(String[] args) throws InterruptedException {
    MyThread mythread = new MyThread();
    System.out.println("begin ==" + mythread.isAlive());
    mythread.start();
    Thread.sleep(1000);
    System.out.println("end ==" + mythread.isAlive());
}

輸出結(jié)果

begin ==falserun=trueend ==false

另外,在使用isAlive() 方法時,如果將線程對象以構(gòu)造參數(shù)的方式傳遞給Thread 對象進行start() 啟動時,運行的結(jié)果和前面示例是有差異的。造成這樣的差異的原因還是來自于Thread.currentThread() 和this 的差異。

/**
 * @program: CountOperate
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 */public class CountOperate extends Thread {    public CountOperate() {
        System.out.println("CountOperate---begin");
        System.out.println("Thread.currentThread().getName()="
                + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="
                + Thread.currentThread().isAlive());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("this.isAlive()=" + this.isAlive());
        System.out.println("CountOperate---end");
    }
    @Override    public void run() {
        System.out.println("run---begin");
        System.out.println("Thread.currentThread().getName()="
                + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="
                + Thread.currentThread().isAlive());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("this.isAlive()=" + this.isAlive());
        System.out.println("run---end");
    }    public static void main(String[] args) {
        CountOperate c = new CountOperate();
        Thread t1 = new Thread(c);
        t1.setName("A");
        t1.start();
    }
}

輸出結(jié)果

CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falseCountOperate---end
run---begin
Thread.currentThread().getName()=A
Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falserun---end

另一種方式

/**
 * @program: CountOperate
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 */public class CountOperate extends Thread {    public CountOperate() {
        System.out.println("CountOperate---begin");
        System.out.println("Thread.currentThread().getName()="
                + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="
                + Thread.currentThread().isAlive());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("this.isAlive()=" + this.isAlive());
        System.out.println("CountOperate---end");
    }
    @Override    public void run() {
        System.out.println("run---begin");
        System.out.println("Thread.currentThread().getName()="
                + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive()="
                + Thread.currentThread().isAlive());
        System.out.println("this.getName()=" + this.getName());
        System.out.println("this.isAlive()=" + this.isAlive());
        System.out.println("run---end");
    }    public static void main(String[] args) {
        CountOperate c = new CountOperate();
       c.start();
    }
}

輸出結(jié)果

CountOperate---begin
Thread.currentThread().getName()=main
Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=falseCountOperate---end
run---begin
Thread.currentThread().getName()=Thread-0Thread.currentThread().isAlive()=truethis.getName()=Thread-0this.isAlive()=truerun---end

sleep() 方法

方法sleep() 的作用是在指定的毫秒數(shù)內(nèi)讓當(dāng)前“正在執(zhí)行的線程”休眠(暫停執(zhí)行)。這個“正在執(zhí)行的線程”是指this.currentThread() 返回的線程。

/**
 * @program: CountOperate
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 */public class CountOperate extends Thread {    @Override
    public void run() {        try {
            System.out.println("run threadName="
                    + Thread.currentThread().getName() + " begin");
            Thread.sleep(2000);
            System.out.println("run threadName="
                    + Thread.currentThread().getName() + " end");
        } catch (InterruptedException e) {// TODO Auto-generated catch block
            e.printStackTrace();
        }
    }    public static void main(String[] args) {
        CountOperate c = new CountOperate();
        c.start();
    }
}

getId() 方法

getId() 方法的作用是取得線程的唯一標識

/**
 * @program: CountOperate
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 */public class CountOperate extends Thread {    public static void main(String[] args) {
        Thread runThread = Thread.currentThread();
        System.out.println(runThread.getName() + " " + runThread.getId());
    }
}# 輸出結(jié)果 main 1

停止線程

停止一個線程可以使用Thread.stop() 方法,但最好不用它。雖然它確
實可以停止一個正在運行的線程,但是這個方法是不安全的(unsafe),而且是已被棄用作廢的(deprecated),在將來的Java 版本中,這個方法將不可用或不被支持。

大多數(shù)停止一個線程的操作使用Thread.interrupt() 方法,盡管方法的名稱是“停止,中止”的意思,但這個方法不會終止一個正在運行的線程,還需要加入一個判斷才可以完成線程的停止。

在Java 中有以下3 種方法可以終止正在運行的線程:

  • 使用退出標志,使線程正常退出,也就是當(dāng)run 方法完成后線程終止。

  • 使用stop 方法強行終止線程,但是不推薦使用這個方法,因為stop 和suspend 及resume 一樣,都是作廢過期的方法,使用它們可能產(chǎn)生不可預(yù)料的結(jié)果。

  • 使用interrupt 方法中斷線程。

停止不了的線程

本示例將調(diào)用interrupt() 方法來停止線程, 但interrupt() 方法的使用效果并不像for+break 語句那樣,馬上就停止循環(huán)。調(diào)用interrupt() 方法僅僅是在當(dāng)前線程中打了一個停止的標記,并不是真的停止線程。

/**
 * @program: MyThread
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 */public class MyThread extends Thread {    @Override
    public void run() {        for (int i = 0; i < 500000; i++) {
            System.out.println("i=" + (i + 1));
        }
    }    public static void main(String[] args) {        try {
            MyThread thread = new MyThread();
            thread.start();
            thread.interrupt();
        } catch (Exception e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

...
...
i=499995
i=499996
i=499997
i=499998
i=499999
i=500000

從運行的結(jié)果來看,調(diào)用interrupt 方法并沒有停止線程。
如何停止線程呢,請繼續(xù)向下讀。

判斷線程是否是停止狀態(tài)

在介紹如何停止線程的知識點前,先來看一下如何判斷線程的狀態(tài)是不是停止的。在Java 的SDK 中,Thread.java 類里提供了兩種方法。

this.interrupted():測試當(dāng)前線程是否已經(jīng)中斷。

this.isInterrupted():測試線程是否已經(jīng)中斷。

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo  extends Thread{    @Override
    public void run() {        for (int i = 0; i < 50; i++) {
            System.out.println("i=" + (i + 1));
        }
    }    public static void main(String[] args) throws InterruptedException {        try {
            Demo thread = new Demo();
            thread.start();
            thread.interrupt();//Thread.currentThread().interrupt();
            System.out.println(" 是否停止1 ? ="+thread.isInterrupted());
            System.out.println(" 是否停止2 ? ="+thread.interrupted());
        } catch (Exception e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }

}

輸出結(jié)果

 是否停止1 ? =true
 是否停止2 ? =false
end!
i=1
...
i=45
i=46
i=47
i=48
i=49
i=50

判斷thread 對象所代表的線程是否停止,但從控制臺打印的結(jié)果來看,線程并未停止,這也就證明了interrupted() 方法的解釋:測試當(dāng)前線程是否已經(jīng)中斷。這個“當(dāng)前線程”是main,它從未中斷過,所以打印的結(jié)果是兩個false。

第二種情況

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo  extends Thread{    @Override
    public void run() {        for (int i = 0; i < 50; i++) {
            System.out.println("i=" + (i + 1));
        }
    }    public static void main(String[] args) throws InterruptedException {        try {
            Demo thread = new Demo();
            thread.start();
            Thread.sleep(1000);
            thread.interrupt();
            System.out.println(" 是否停止1 ? ="+thread.isInterrupted());
            System.out.println(" 是否停止2 ? ="+thread.interrupted());
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }

}

輸出結(jié)果

i=1
...
i=47
i=48
i=49
i=50
 是否停止1 ? =false
 是否停止2 ? =false
end!

為什么兩種結(jié)果不一樣呢,因為休息一秒后,線程早已經(jīng)結(jié)束了,所以不能中斷了,interrupted() 方法的解釋:測試當(dāng)前線程是否已經(jīng)中斷。等價于

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo  extends Thread{    @Override
    public void run() {        for (int i = 0; i < 50; i++) {
            System.out.println("i=" + (i + 1));
        }
    }    public static void main(String[] args) throws InterruptedException {        try {
            Demo thread = new Demo();
            thread.start();
            Thread.sleep(1000);
            thread.interrupt();
            System.out.println(" 是否停止1 ? ="+thread.isInterrupted());
            System.out.println(" 是否停止2 ? ="+Thread.interrupted());
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }

}

最后,再來看一下這兩個方法的解釋。

this.interrupted() :測試當(dāng)前線程是否已經(jīng)是中斷狀態(tài),
執(zhí)行后具有將狀態(tài)標志置清除為false 的功能。

this.isInterrupted() :測試線程Thread 對象是否已經(jīng)
是中斷狀態(tài),但不清除狀態(tài)標志。

能停止的線程——異常法

直接上代碼

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo extends Thread {    @Override
    public void run() {        super.run();        for (int i = 0; i < 5000; i++) {            if (Thread.interrupted()) {
                System.out.println(" 已經(jīng)是停止狀態(tài)了! 我要退出了!");                break;
            }
            System.out.println("i=" + (i + 1));
        }        //System.out.println(" 我被輸出,如果此代碼是for 又繼續(xù)運行,線程并未停止! ");
    }    public static void main(String[] args) throws InterruptedException {
        Demo thread = new Demo();
        thread.start();
        thread.interrupt();
        System.out.println(" 是否停止1 ? =" + thread.isInterrupted());
        System.out.println(" 是否停止2 ? =" + Thread.interrupted());
        System.out.println("end!");
    }

}

輸出結(jié)果

 是否停止1 ? =true
 是否停止2 ? =falseend!
 已經(jīng)是停止狀態(tài)了! 我要退出了!

上面的示例雖然停止了線程,但如果for 語句下面還有語句,還是會繼續(xù)運行的。

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo extends Thread {
    @Override    public void run() {        try {            for (int i = 0; i < 500000; i++) {                if (Thread.interrupted()) {
                    System.out.println(" 已經(jīng)是停止狀態(tài)了! 我要退出了!");                    throw new InterruptedException();
                }
                System.out.println("i=" + (i + 1));
            }
            System.out.println(" 我在for 下面");
        } catch (InterruptedException e) {
            System.out.println(" 進MyThread.java 類run 方法中的catch 了! ");
            e.printStackTrace();
        }
    }    public static void main(String[] args) throws InterruptedException {
        Demo thread = new Demo();
        thread.start();
        thread.interrupt();
        System.out.println(" 是否停止1 ? =" + thread.isInterrupted());
        System.out.println(" 是否停止2 ? =" + Thread.interrupted());
        System.out.println("end!");
    }

}

輸出結(jié)果

是否停止1 ? =true是否停止2 ? =falseend!
已經(jīng)是停止狀態(tài)了! 我要退出了!
進MyThread.java 類run 方法中的catch 了! 
java.lang.InterruptedException
    at Demo.run(Demo.java:14)

在沉睡中停止

如果在sleep狀態(tài)下停止某一線程,會進入catch語句,并且清除停止狀態(tài)值,使之變成false.

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo extends Thread {    @Override
    public void run() {        try {
            System.out.println("run begin");
            Thread.sleep(200000);
            System.out.println("run end");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中被停止! 進入catch!"+this.isInterrupted());
            e.printStackTrace();
        }
    }    public static void main(String[] args) throws InterruptedException {        try {
            Demo thread = new Demo();
            thread.start();
            Thread.sleep(200);
            thread.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }

}

輸出結(jié)果

run beginend!
在沉睡中被停止! 進入catch!falsejava.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)    at Demo.run(Demo.java:12)

另一種情況

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo extends Thread {    @Override
    public void run() {        try {            for(int i=0;i<100000;i++){
                System.out.println("i="+(i+1));
            }
            System.out.println("run begin");
            Thread.sleep(200000);
            System.out.println("run end");
        } catch (InterruptedException e) {
            System.out.println(" 先停止,再遇到了sleep! 進入catch!");
            e.printStackTrace();
        }
    }    public static void main(String[] args) throws InterruptedException {
        Demo thread = new Demo();
        thread.start();
        thread.interrupt();
        System.out.println("end!");
    }

}

輸出結(jié)果

...
i=99998
i=99999
i=100000
run begin
 先停止,再遇到了sleep! 進入catch!

能停止的線程——暴力停止

使用stop() 方法停止線程則是非常暴力的,調(diào)用stop() 方法時會拋出java.lang.ThreadDeath 異常,但在通常的情況下,此異常不需
要顯式地捕捉,方法stop() 已經(jīng)被作廢,因為如果強
制讓線程停止則有可能使一些清理性的工
作得不到完成。另外一個情況就是對鎖定
的對象進行了“解鎖”,導(dǎo)致數(shù)據(jù)得不到
同步的處理,出現(xiàn)數(shù)據(jù)不一致的問題。

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo extends Thread {    @Override
    public void run() {        try {            for(int i=0;i<100000;i++){
                System.out.println("i="+(i+1));
                Thread.sleep(2000);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }    public static void main(String[] args) throws InterruptedException {
        Demo thread = new Demo();
        thread.start();
        Thread.sleep(10000);
        thread.stop();
        System.out.println("end!");
    }

}

輸出結(jié)果

i=1
i=2
i=3
i=4
i=5
end!

stop停止導(dǎo)致數(shù)據(jù)不一致

/**
 * Created by Lee on 2018/7/28.
 */public class StopSynchronizedObject {    private String username = "a";    private String password = "aa";    public String getUsername() {        return username;
    }    public void setUsername(String username) {        this.username = username;
    }    public String getPassword() {        return password;
    }    public void setPassword(String password) {        this.password = password;
    }    synchronized public void printString(String username, String password) {        try {            this.username = username;
            Thread.sleep(8000);            this.password = password;
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}/**
 * Created by Lee on 2018/7/28.
 */public class StopSynchronizedThread extends Thread {    private StopSynchronizedObject object;    public  StopSynchronizedThread(StopSynchronizedObject object) {        this.object=object;
    }
    @Override    public void run() {        object.printString("b","bb");
    }
}/**
 * Created by Lee on 2018/7/28.
 */public class StopSynchronizedRun {    public static void main(String[] args) {        try {
            StopSynchronizedObject object = new StopSynchronizedObject();
            StopSynchronizedThread thread = new StopSynchronizedThread(object);
            thread.start();
            Thread.sleep(500);
            thread.stop();
            System.out.println(object.getUsername());
            System.out.println(object.getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

baa

再看一個例子

/**
 * Created by Lee on 2018/7/28.
 */public class StopSynchronizedRun {    public static void main(String[] args) {        try {
            StopSynchronizedObject object = new StopSynchronizedObject();
            StopSynchronizedThread thread = new StopSynchronizedThread(object);
            thread.start();            //Thread.sleep(500);
            thread.stop();
            System.out.println(object.getUsername());
            System.out.println(object.getPassword());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

aaa

使用return 停止線程

/**
 * @program: demo
 * @description: java基礎(chǔ)知識測試
 * @author: lee
 * @create: 2018-07-18 09:45
 **/public class Demo extends Thread {    @Override
    public void run() {        while (true) {            if (this.isInterrupted()) {
                System.out.println("停止了!");                return;
            }
            System.out.println("timer=" + System.currentTimeMillis());
        }
    }    public static void main(String[] args) throws InterruptedException {
        Demo thread = new Demo();
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
        System.out.println("end!");
    }

}

輸出結(jié)果

timer=1532932687078
timer=1532932687078
end!
停止了!

暫停線程

暫停線程意味著此線程還可以恢復(fù)運行。在Java 多線程中,可以使用suspend() 方法暫停線程,使用resume() 方法恢復(fù)線程的執(zhí)行。

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-07-29 11:05
 **/public class SuspendThread extends Thread {    private long i = 0;    public long getI() {        return i;
    }    public void setI(long i) {        this.i = i;
    }    @Override
    public void run(){        while (true) {
            i++;
        }
    }
}    

/**
 * Created by Lee on 2018/7/28.
 */public class SuspendThreadRun {    public static void main(String[] args) {        try {
            SuspendThread thread = new SuspendThread();
            thread.start();
            Thread.sleep(1000);            //第一段
            thread.suspend();
            System.out.println("I="+thread.getI());
            Thread.sleep(5000);
            System.out.println("I="+thread.getI());            //第二段
            thread.resume();
            Thread.sleep(1000);            //第三段
            thread.suspend();
            System.out.println("I="+thread.getI());
            Thread.sleep(5000);
            System.out.println("I="+thread.getI());
            thread.stop();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

輸出結(jié)果

I=483701768I=483701768I=1001747085I=1001747085

suspend 與resume 方法的缺點——獨占

在使用suspend 與resume 方法時,如果使用不當(dāng),極易造成公共的同步對象的獨占,使得其他線程無法訪問公共同步對象。

public class SynchronizedObject {    synchronized public void printString() {
        System.out.println("begin");        if (Thread.currentThread().getName().equals("a")) {
            System.out.println("a 線程永遠 suspend 了! ");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }
}public class Run {    public static void main(String[] args) {        try {
            final SynchronizedObject object = new SynchronizedObject();
            Thread thread1 = new Thread() {
                @Override                public void run() {                    object.printString();
                }
            };
            thread1.setName("a");
            thread1.start();
            Thread.sleep(1000);
            Thread thread2 = new Thread() {
                @Override                public void run() {
                    System.out.println("thread2 啟動了,但進入不了printString() 方法! 只打印1 個begin");
                    System.out.println(" 因為printString() 方法被a 線程鎖定并且永遠 suspend 暫停了! ");                    object.printString();
                }
            };
            thread2.start();
        } catch (InterruptedException e) {// TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

還有另外一種獨占鎖的情況也要格外注意,稍有不慎,就會掉進“坑”里

public class MyThread extends Thread {    private long i = 0;    @Override
    public void run() {        while (true) {
            i++;
        }
    }
}    


public class Run {    public static void main(String[] args) {        try {
            MyThread thread = new MyThread();
            thread.start();
            Thread.sleep(1000);
            thread.suspend();
            System.out.println("main end!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

main end!

如果更改程序如下

/**
 * @program: demo
 * @description: 2
 * @author: lee
 * @create: 2018-07-30 14:52
 **/public class test extends Thread{    private long i = 0;    @Override
    public void run() {        while (true) {
            i++;
            System.out.println(i);
        }
    }    public static void main(String[] args) {        try {
            test thread = new test();
            thread.start();
            Thread.sleep(1000);
            thread.suspend();
            System.out.println("main end!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

...
113720
113721
113722
113723

不再會有main end!輸出,出現(xiàn)這樣情況的原因是,當(dāng)程序運行到println() 方法內(nèi)部停止時,同步鎖未被釋放。

 /**
     * Prints a String and then terminate the line.  This method behaves as
     * though it invokes <code>{@link #print(String)}</code> and then
     * <code>{@link #println()}</code>.
     *
     * @param x  The <code>String</code> to be printed.
     */
    public void println(String x) {        synchronized (this) {
            print(x);
            newLine();
        }
    }

suspend 與resume 方法的缺點——不同步

在使用suspend 與resume 方法時也容易出現(xiàn)因為線程的暫停而導(dǎo)致數(shù)據(jù)不同步的情況。

public class MyObject {    private String username = "1";    private String password = "11";    public void setValue(String u, String p) {        this.username = u;        if (Thread.currentThread().getName().equals("a")) {
            System.out.println(" 停止a 線程! ");
            Thread.currentThread().suspend();
        }        this.password = p;
    }    public void printUsernamePassword() {
        System.out.println(username + " " + password);
    }
}public class Run {    public static void main(String[] args) throws InterruptedException {
        final MyObject myobject = new MyObject();
        Thread thread1 = new Thread() {            public void run() {
                myobject.setValue("a", "aa");
            };
        };
        thread1.setName("a");
        thread1.start();
        Thread.sleep(500);
        Thread thread2 = new Thread() {            public void run() {
                myobject.printUsernamePassword();
            };
        };
        thread2.start();
    }
}

輸出結(jié)果

停止a線程
a 11

yield 方法

yield() 方法的作用是放棄當(dāng)前的CPU 資源,將它讓給其他的任務(wù)去占用CPU 執(zhí)行時間。但放棄的時間不確定,有可能剛剛放棄,馬上又獲得CPU 時間片。

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-07-29 11:21
 **/public class YieldThread extends Thread {
    @Override    public void run(){        long beginTime = System.currentTimeMillis();        int count = 0;        for (int i = 0; i < 5000000; i++) {           // Thread.yield();
            count = count + (i+1);
        }        long endTime=System.currentTimeMillis();
        System.out.println("用時:" + (endTime-beginTime) + "毫秒!");
    }    public static void main(String[] args) {
        YieldThread thread = new YieldThread();
        thread.start();
    }
}    
# 輸出結(jié)果 用時:2毫秒!# 沒有注釋Thread.yield();輸出結(jié)果 用時:1809毫秒!

線程的優(yōu)先級

在操作系統(tǒng)中,線程可以劃分優(yōu)先級,優(yōu)先級較高的線程得到的CPU 資源較多,也就是CPU 優(yōu)先執(zhí)行優(yōu)先級較高的線程對象中的任務(wù),在Java 中,線程的優(yōu)先級分為1 ~ 10 這10 個等級,如果小于1 或大于10,則JDK 拋出異常throw new IllegalArgumentException()。

JDK 中使用3 個常量來預(yù)置定義優(yōu)先級的值,代碼如下:

public final static int MIN_PRIORITY = 1;public final static int NORM_PRIORITY = 5;public final static int MAX_PRIORITY = 10;

線程優(yōu)先級的繼承特性

在Java 中,線程的優(yōu)先級具有繼承性,比如A 線程啟動B 線程,則B 線程的優(yōu)先級與A 是一樣的。

 /**
 * @program: demo
 * @description: MyThread1
 * @author: lee
 * @create: 2018-07-30 15:38
 **/public class MyThread1 extends Thread {    @Override
    public void run() {
        System.out.println("MyThread1 run priority=" + this.getPriority());
        MyThread2 thread2 = new MyThread2();
        thread2.start();
    }
}/**
 * @program: demo
 * @description: MyThread2
 * @author: lee
 * @create: 2018-07-30 15:38
 **/public class MyThread2 extends Thread {    @Override
    public void run() {
        System.out.println("MyThread2 run priority=" + this.getPriority());
    }
}/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-07-30 15:39
 **/public class RunDemo {    public static void main(String[] args) {
        MyThread1 thread = new MyThread1();
        thread.start();
    }
}

輸出結(jié)果

MyThread1 run priority=5MyThread2 run priority=5

優(yōu)先級具有規(guī)則性

雖然使用setPriority() 方法可以設(shè)置線程的優(yōu)先級,但還沒有看到設(shè)置優(yōu)先級所帶來的效果。

import java.util.Random;/**
 * @program: demo
 * @description: 線程優(yōu)先級測試
 * @author: lee
 * @create: 2018-07-29 11:37
 **/public class PropertyThread1 extends Thread{    @Override
    public void run() {        long beginTime = System.currentTimeMillis();        long addResult = 0;        for (int j = 0;j < 100; j++) {
            Random random = new Random();
            random.nextInt();
            addResult = addResult +j;            long endTime = System.currentTimeMillis();
            System.out.println("***************  PropertyThread1 use time= " +(endTime-beginTime));
        }
    }
}import java.util.Random;/**
 * @program: demo
 * @description: 線程優(yōu)先級測試
 * @author: lee
 * @create: 2018-07-29 11:37
 **/public class PropertyThread2 extends Thread{    @Override
    public void run() {        long beginTime = System.currentTimeMillis();        long addResult = 0;        for (int j = 0;j < 100; j++) {
            Random random = new Random();
            random.nextInt();
            addResult = addResult +j;            long endTime = System.currentTimeMillis();
            System.out.println("#########  PropertyThread2 use time= " +(endTime-beginTime));
        }

    }
}/**
 * @program: demo
 * @description: property測試
 * @author: lee
 * @create: 2018-07-29 11:43
 **/public class PropertyRun {    public static void main(String[] args) {
        PropertyThread1 thread1 = new PropertyThread1();
        thread1.setPriority(2);
        thread1.start();
        PropertyThread2 thread2 = new PropertyThread2();
        thread2.start();
        thread2.setPriority(6);
    }
}

輸出結(jié)果

...#########  PropertyThread2 use time= 1***************  PropertyThread1 use time= 2#########  PropertyThread2 use time= 2***************  PropertyThread1 use time= 3#########  PropertyThread2 use time= 2...***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11***************  PropertyThread1 use time= 11

高優(yōu)先級的線程總是大部分先執(zhí)行完,但不代表高優(yōu)先級的線
程全部先執(zhí)行完。另外,不要以為thread1 線程先被main 線程所調(diào)用就會先執(zhí)行完,當(dāng)線程優(yōu)先級的等級差距很大時,誰先執(zhí)行完和代碼的調(diào)用順序無關(guān),線程的優(yōu)先級具有一定的規(guī)則性,也就是CPU 盡量將執(zhí)行資源讓給優(yōu)先級比較高的線程。

優(yōu)先級具有隨機性

線程的優(yōu)先級還具有“隨機性”,也就是優(yōu)先級較高的線程不一定每一次
都先執(zhí)行完,當(dāng)優(yōu)先級的差距不大的時候最能體現(xiàn)出隨機性。

看誰運行得快

線程的優(yōu)先級越高,CPU分配得到的機會越大,所以執(zhí)行的時間越多。

守護線程

在Java 線程中有兩種線程,一種是用戶線程,另一種是守護線程。

守護線程是一種特殊的線程,它的特性有“陪伴”的含義,當(dāng)進程中不存在非守護線程了,則守護線程自動銷毀。典型的守護線程就是垃圾回收線程,當(dāng)進程中沒有非守護線程了,則垃圾回收線程也就沒有存在的必要了,自動銷毀。

用個比較通俗的比喻來解釋一下
“守護線程”:任何一個守護線程都是整個JVM 中所有非守護線程的“保姆”,只要當(dāng)前JVM
實例中存在任何一個非守護線程沒有結(jié)束,守護線程就在工作,只有當(dāng)最后一個非守護線程
結(jié)束時,守護線程才隨著JVM 一同結(jié)束工作。
注意點:

thread.setDaemon(true)必須在thread.start()之前設(shè)置。

在Daemon線程中產(chǎn)生的新線程也是Daemon的。

不是所有的應(yīng)用都可以分配給Daemon線程來進行服務(wù),比如讀寫操作或者計算邏輯。因為Daemon Thread還沒來得及進行操作,虛擬機可能已經(jīng)退出了。
/**
 * @program: demo
 * @description: 守護線程
 * @author: lee
 * @create: 2018-07-29 12:04
 **/public class DaemonThread extends Thread{    private int  i = 0;    @Override
    public void run() {        while (true) {            try {
                System.out.println(i++);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}/**
 * @program: demo
 * @description: 守護進程
 * @author: lee
 * @create: 2018-07-29 12:06
 **/public class DaemonRun {    public static void main(String[] args) throws InterruptedException {
        DaemonThread thread = new DaemonThread();
        thread.setDaemon(true);
        thread.start();
        Thread.sleep(5000);
   System.out.println("***************************************************************再也不會打印東西了。。。。");
    }
}

輸出結(jié)果

0
1
2
3
4***************************************************************再也不會打印東西了。。。。

如果多線程類改變一下

/**
 * @program: demo
 * @description: 守護線程
 * @author: lee
 * @create: 2018-07-29 12:04
 **/public class DaemonThread extends Thread{    private int  i = 0;    @Override
    public void run() {        while (true) {
            System.out.println(i++);
        }
    }
}

輸出結(jié)果

...
106250
106251
106252***************************************************************再也不會打印東西了。。。。
106253
106254
106255
...
106341
Process finished with exit code 0


標簽: PHP
分享:
評論:
你還沒有登錄,請先