JAVA多線程入門

繼承Thread父類

線程代碼執(zhí)行順序和調(diào)用順序無關(guān),例如:

public class MyThread extends Thread {

    @Override
    public void run(){
        super.run();
        System.out.println("MyThread");
    }
/**運(yùn)行順序存疑
 * 并沒有發(fā)現(xiàn)隨機(jī)性 */
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.run();
        System.out.println("mainThread");

    }
    
}

上述代碼執(zhí)行理論上“MyThread”和“mainThread”打印順序是隨機(jī)的,和調(diào)用順序無關(guān),實(shí)際情況存疑。

線程執(zhí)行具有隨機(jī)性,CPU的執(zhí)行具有不確定性

public class MyThread1 extends Thread {

    @Override
    public void run(){
        try {
            for (int i = 0;i < 10;i++){
            int time = (int) (Math.random()*1000);
            Thread.sleep(time);
            System.out.println("run:"+Thread.currentThread().getName());
            }
        }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    public static void main(String[] args) throws InterruptedException {
        MyThread1 thread = new MyThread1();
        thread.setName("myThread");
        thread.start();
        for (int i = 0; i<10;i++){
            int time = (int) (Math.random()*1000);
                Thread.sleep(time);
                System.out.println("main:"+Thread.currentThread().getName());
        }

    }
}

/*結(jié)果:
run:myThread
run:myThread
run:myThread
main:main
main:main
run:myThread
run:myThread
run:myThread
main:main
main:main
main:main
main:main
run:myThread
run:myThread
main:main
run:myThread
run:myThread
main:main
main:main
main:main
*/

start方法并不代表線程啟動(dòng),線程啟動(dòng)順序由CPU執(zhí)行順序決定,無序性。

public class MyThread2 extends Thread {
    private int i;
    public MyThread2(int i){
        super();
        this.i = i;
    }
    @Override
    public void run(){
        System.out.println("myThread:"+i);
    }

    public static void main(String[] args){
        MyThread2 t1 = new MyThread2(1);
        MyThread2 t2 = new MyThread2(2);
        MyThread2 t3 = new MyThread2(3);
        MyThread2 t4 = new MyThread2(4);
        MyThread2 t5 = new MyThread2(5);
        MyThread2 t6 = new MyThread2(6);
        MyThread2 t7 = new MyThread2(7);
        MyThread2 t8 = new MyThread2(8);
        MyThread2 t9 = new MyThread2(9);
        MyThread2 t10 = new MyThread2(10);
        MyThread2 t11 = new MyThread2(11);
        MyThread2 t12 = new MyThread2(12);
        MyThread2 t13 = new MyThread2(13);
        MyThread2 t14 = new MyThread2(14);
        MyThread2 t15 = new MyThread2(15);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
        t7.start();
        t8.start();
        t9.start();
        t10.start();
        t11.start();
        t12.start();
        t13.start();
        t14.start();
        t15.start();
    }
}

/*結(jié)果:
myThread:2
myThread:1
myThread:3
myThread:4
myThread:7
myThread:8
myThread:11
myThread:12
myThread:15
myThread:13
myThread:14
myThread:5
myThread:6
myThread:9
myThread:10
 
*/

Runnable接口構(gòu)造線程

java是單基礎(chǔ),繼承Thread類有局限性,所以更多的是使用Runnable接口去新建線程,Thread類有構(gòu)造方法使用Runnable接口新建線程。

public class RunableTest implements Runnable {

    @Override
    public void run() {
        System.out.println("Runable線程運(yùn)行中:"+Thread.currentThread().getName());
    }

    public static void main(String[] arg){
        RunableTest runableTest = new RunableTest();
        Thread thread = new Thread(runableTest);
        thread.start();
        System.out.println("mainThread:"+Thread.currentThread().getName() );
    }
}

實(shí)例變量與線程安全

實(shí)例變量不共享

線程間變量不共享,數(shù)據(jù)不共享情況:

public class ShareThread extends Thread{
    private int count = 5;
    public ShareThread(String name){
        super();
        this.setName(name);
    }
    @Override
    public void run(){
        super.run();
        while (count >0){
            count--;
            System.out.println("由"+Thread.currentThread().getName()+"計(jì)算,count="+count);
        }
    }

    public static void main(String[] args){
        ShareThread shareThread1 = new ShareThread("A");
        ShareThread shareThread2 = new ShareThread("B");
        ShareThread shareThread3 = new ShareThread("C");
        shareThread1.start();
        shareThread2.start();
        shareThread3.start();
    }
}

/*
由A計(jì)算,count=4
由B計(jì)算,count=4
由B計(jì)算,count=3
由B計(jì)算,count=2
由B計(jì)算,count=1
由B計(jì)算,count=0
由A計(jì)算,count=3
由A計(jì)算,count=2
由A計(jì)算,count=1
由A計(jì)算,count=0
由C計(jì)算,count=4
由C計(jì)算,count=3
由C計(jì)算,count=2
由C計(jì)算,count=1
由C計(jì)算,count=0
*/

線程數(shù)據(jù)共享

共享數(shù)據(jù)情況就是多個(gè)線程可以訪問同一個(gè)變量。

public class ShareThread1 extends Thread {
    private int count  = 10;

    @Override
    synchronized public void run(){
        super.run();
        count--;
        //不要使用for語句,因?yàn)槭褂猛胶缶€程就沒有運(yùn)行機(jī)會(huì)了
        //一直由線程進(jìn)行減法運(yùn)算
        System.out.println("由"+Thread.currentThread().getName()+"計(jì)算,count="+count);
    }

    public static void main(String[] args){
        ShareThread1 thread1 = new ShareThread1();
        Thread a = new Thread(thread1,"A");
        Thread b = new Thread(thread1,"B");
        Thread c = new Thread(thread1,"C");
        Thread d = new Thread(thread1,"D");
        Thread e = new Thread(thread1,"E");
        Thread f = new Thread(thread1,"F");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
        f.start();
    }

}


/*結(jié)果:
由A計(jì)算,count=9
由D計(jì)算,count=8
由E計(jì)算,count=7
由F計(jì)算,count=6
由C計(jì)算,count=5
由B計(jì)算,count=4
*/

synchronized關(guān)鍵字表示執(zhí)行多個(gè)線程時(shí)以排隊(duì)的方式進(jìn)行處理。線程執(zhí)行時(shí)會(huì)上鎖,執(zhí)行完畢后會(huì)解鎖,線程調(diào)用run()方法前會(huì)請求線程鎖,若已經(jīng)上鎖,則會(huì)不斷請求線程鎖。

System.out.println()使用時(shí)可能會(huì)發(fā)生“非線程安全”問題,里面打印i--時(shí),會(huì)先執(zhí)行i--,然后打印結(jié)果,造成線程安全問題。

public class ShareThread2 extends Thread {
    private int i = 5;
    @Override
    public void run(){
        System.out.println("i="+ (i--) +",threadName="+Thread.currentThread().getName());
    //i--在println之前執(zhí)行,故可能發(fā)生非線程安全問題
    }

    public static void main(String[] args) {
        ShareThread2 run = new ShareThread2();
        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();
    }
}

/*
i=4,threadName=Thread-1
i=5,threadName=Thread-3
i=2,threadName=Thread-5
i=5,threadName=Thread-4
i=3,threadName=Thread-2

*/

常用函數(shù)

currentThread()方法

currentThread返回代碼段被哪個(gè)線程調(diào)用的信息。

public class CountOpertrate extends Thread {
    public CountOpertrate(){
        System.out.println("CountOpertate-build-start");
        System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName());
        System.out.println("this.getName()"+this.getName());
        System.out.println("CountOpertrate-build-end");
    }

    @Override
    public void run(){
        System.out.println("run-start");
        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) {
        CountOpertrate countOpertrate = new CountOpertrate();
        Thread t = new Thread(countOpertrate);
        t.setName("TEST");
        t.start();
    }

/*result:
CountOpertate-build-start
Thread.currentThread().getName() = main
this.getName()Thread-0
CountOpertrate-build-end
run-start
Thread.currentThread().getName() = TEST
this.getName()Thread-0
run-end
*/

上述代碼顯示,Count構(gòu)建時(shí)時(shí)用的main線程,run是跑在TEST線程上。

isAlive()方法

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

public class IsAliveTest extends Thread {
    @Override
    public void run() {
        System.out.println("run = "+this.isAlive());
    }

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

/*result:
start ==false
run = true
end ==false
*/

若將線程對象以構(gòu)造參數(shù)傳遞給Thread對象進(jìn)行start,結(jié)果會(huì)有差異。

public class IsAliveTest1 extends Thread {

    public IsAliveTest1(){
        System.out.println("IsAliveTest1-Start");
        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("IsAliveTest1-end");
    }
    @Override
    public void run(){
        System.out.println("run-Start");
        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) throws InterruptedException {
        IsAliveTest1 test1 = new IsAliveTest1();
        Thread t1 = new Thread(test1);
        System.out.println("main bigin t1 isAlive = "+t1.isAlive());
        t1.setName("AAA");
        t1.start();
        Thread.sleep(1000);
        System.out.println("main end t1 isAlive = "+t1.isAlive());
    }
}

/*result:
IsAliveTest1-Start
Thread.currentThread().getName() = main
Thread.currentThread().isAlive() = true
this.getName() = Thread-0
this.isAlive() = false
IsAliveTest1-end
main bigin t1 isAlive = false
run-Start
Thread.currentThread().getName() = AAA
Thread.currentThread().isAlive() = true
this.getName() = Thread-0
this.isAlive() = false
run-end
main end t1 isAlive = false
*/

sleep()方法

sleep()方法是在括號中毫秒內(nèi)使正在執(zhí)行的線程暫停執(zhí)行的方法,正在執(zhí)行的線程是this.currentThread()返回的線程。

public class sleepTest extends Thread {
    @Override
    public void run() {
        try {
            System.out.println("run threadName = "+this.getName()+"-begin");
            Thread.sleep(2000);
            System.out.println("run ThreadName = "+this.getName()+"-end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        sleepTest test = new sleepTest();
        System.out.println("begin = "+ System.currentTimeMillis());
        test.run();
        //test.start();
        System.out.println("end = "+System.currentTimeMillis());
    }
}

/*直接用run()方法
begin = 1521341785159
run threadName = Thread-0-begin
run ThreadName = Thread-0-end
end = 1521341787160
*/
/*使用start()方法
begin = 1521341902632
end = 1521341902632
run threadName = Thread-0-begin
run ThreadName = Thread-0-end

main和sleepTest線程是異步的,所以先打印時(shí)間
*/

停止線程

interrupt()方法

interrupt方法并不是立刻停止線程。而是在當(dāng)前線程中打一個(gè)停止標(biāo)記。

public class InterruptTest extends Thread {

    @Override
    public void run() {
        super.run();
        for (int i = 0; i<50000;i++){
            System.out.println("i = "+ (i+1));
        }
    }

    public static void main(String[] args) {
        try {
            InterruptTest test = new InterruptTest();
            test.start();
            Thread.sleep(2000);
            Thread.interrupted();
        } catch (InterruptedException e) {
            System.out.println("main-catch");
            e.printStackTrace();
        }
    }
}
/*
無法停止,打印50000條記錄
*/

判斷線程是否是停止?fàn)顟B(tài)

interrupted()方法,測試當(dāng)前線程是否已經(jīng)是中斷狀態(tài),執(zhí)行后將狀態(tài)標(biāo)志改為false。
isInterrupted()方法,測試線程對象是否已經(jīng)為中斷狀態(tài),但不清除狀態(tài)標(biāo)志。

異常法停止線程

可以使用isInterrupted方法判斷線程停止標(biāo)志狀態(tài)并拋出InterruptedException,使用interrupt()方法停止線程后,因?yàn)榻邮盏酵V範(fàn)顟B(tài)碼,拋出異常進(jìn)入catch分支,繼而終止線程。

public class StopThreadTest extends Thread {

    @Override
    public void run() {
        super.run();
        try {
            for (int i=0;i<1000000;i++){
                if (this.isInterrupted()){
                    System.out.println("已是停止?fàn)顟B(tài),線程退出!");
                    throw new InterruptedException();
                }
                System.out.println("i = "+(i+1));
            }
            System.out.println("for下面的");
        } catch (InterruptedException e) {
            System.out.println("線程run()方法catch!線程異常終止");
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {

        try {
            StopThreadTest test = new StopThreadTest();
            test.start();
            Thread.sleep(1000);
            test.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main  catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }

}

/*result:
... ...
i = 274280
i = 274281
i = 274282
i = 274283
i = 274284
end!
已是停止?fàn)顟B(tài),線程退出!
線程run()方法catch!線程異常終止
java.lang.InterruptedException
    at com.tz.StopThread.StopThreadTest.run(StopThreadTest.java:15)

*/

沉睡中停止進(jìn)程

線程在sleep狀態(tài)下停止,會(huì)直接報(bào)異常,并進(jìn)入catch退出,有兩種情況,一個(gè)是先sleep再interrupt,還有就是先interrupt再停止。

//先sleep

public class StopSleep1 extends Thread{

    @Override
    public void run() {
        super.run();
        try {
            System.out.println("run-begin");
            Thread.sleep(200000);
            System.out.println("run-end");
        } catch (InterruptedException e) {
            System.out.println("在沉睡中停止,run()進(jìn)入catch  "+this.isInterrupted());
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        try {
            StopSleep1 sleep1 = new StopSleep1();
            sleep1.start();
            Thread.sleep(200);
            sleep1.interrupt();
        } catch (InterruptedException e) {
            System.out.println("main-catch");
            e.printStackTrace();
        }
        System.out.println("end!");
    }
}

/*result:
run-begin
end!
在沉睡中停止,run()進(jìn)入catch  false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.tz.StopThread.StopSleep1.run(StopSleep1.java:13)
*/
//后sleep

public class StopSleep2 extends Thread {

    @Override
    public void run() {
        super.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,run()進(jìn)入catch  "+this.isInterrupted());
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

            StopSleep2 sleep2 = new StopSleep2();
            sleep2.start();
            sleep2.interrupt();
            System.out.println("end!");
    }

}

/*result:
i = 99997
i = 99998
i = 99999
i = 100000
run-begin
先停止再遇到sleep,run()進(jìn)入catch  false
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.tz.StopThread.StopSleep2.run(StopSleep2.java:16)
*/

暴力停止線程

使用stop方法停止線程,這個(gè)方法很暴力。

public class StopThread extends Thread {

    private int i = 0;

    @Override
    public void run() {
        try {
            while (true){
                i++;
                System.out.println("i=" +i);
                Thread.sleep(1000);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            StopThread thread = new StopThread();
            thread.start();
            Thread.sleep(8000);
            thread.stop();
            System.out.println("stop暴力停止");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

stop方法已經(jīng)作廢,盡量不使用?。。?/strong>

stop方法釋放鎖,會(huì)造成數(shù)據(jù)不一致的結(jié)果。

public class StopThread1 extends Thread {

    private SynchronizedObject object;

    public StopThread1(SynchronizedObject object){
        super();
        this.object = object;
    }

    @Override
    public void run() {
        object.printString("b","bb");
    }


    public static void main(String[] args) {

        try {
            SynchronizedObject object = new SynchronizedObject();
            StopThread1 thread1 = new StopThread1(object);
            thread1.start();
            Thread.sleep(500);
            thread1.stop();
            System.out.println("object.getUsername()="+object.getUsername());
            System.out.println("object.getPassword()="+object.getPassword());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

/*result:
object.getUsername()=b
object.getPassword()=aa
*/

return 停止線程

可以將interrupt()方法與return結(jié)合實(shí)現(xiàn)停止線程。

public class ReturnStopThread 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 {
        ReturnStopThread thread = new ReturnStopThread();
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();
    }
}

/*result:
... ...
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
timer = 1521358261861
停止!
*/

建議還是使用拋異常來停止進(jìn)程,因?yàn)閽伄惓?梢酝ㄟ^catch語句將線程停止事件上拋,是線程停止事件得以傳播。

暫停線程

暫停線程意味著次現(xiàn)場可以恢復(fù)運(yùn)行,在Java多線程中可以使用suspend()方法暫停線程,使用resume()方法恢復(fù)線程的執(zhí)行。

public class SuspendTestThread 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++;
        }
    }


    public static void main(String[] args) {
        try {
            SuspendTestThread thread = new SuspendTestThread();
            thread.start();
            Thread.sleep(1000);
            //A段
            thread.suspend();
            System.out.println("線程暫停!");
            System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI());
            Thread.sleep(1000);
            System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI());

            //B段
            thread.resume();
            Thread.sleep(1000);
            System.out.println("線程喚醒!");
            System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI());
            Thread.sleep(1000);
            System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI());

            //c段
            thread.suspend();
            System.out.println("線程又暫停");
            System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI());
            Thread.sleep(1000);
            System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI());

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
/*result:
線程暫停!
A= 1521516350803 i=620113660
A= 1521516351803 i=620113660
線程喚醒!
B= 1521516352804 i=1259895883
B= 1521516353804 i=1901121177
線程又暫停
C= 1521516353804 i=1901176584
C= 1521516354804 i=1901176584

*/

明顯線程在A和C段暫停執(zhí)行了,在B段喚醒之后又能重新執(zhí)行。

suspend和rusume的缺點(diǎn)

  1. 獨(dú)占

使用線程暫停時(shí),如果使用不當(dāng),容易造成對公共的同步對象的獨(dú)占,導(dǎo)致其他線程無法訪問公共同步對象。

//model.class
public class SynchronizedObject {
    synchronized public void printString(){
        System.out.println("begin");
        if (Thread.currentThread().getName().equals("a")){
            System.out.println("a線程永久陷入沉睡!");
            Thread.currentThread().suspend();
        }
        System.out.println("end");
    }
}

public class SuspendTestThread1 extends Thread {
    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("thraed2啟動(dòng),但進(jìn)入不了printString()方法");
                    System.out.println("因?yàn)閜rintString()方法被a線程鎖定并獨(dú)占了");
                    object.printString();
                }
            };
            thread2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
 /*result:
 begin
a線程永久陷入沉睡!
thraed2啟動(dòng),但進(jìn)入不了printString()方法
因?yàn)閜rintString()方法被a線程鎖定并獨(dú)占了
 */

  1. 不同步
    因?yàn)榫€程暫??赡軙?huì)導(dǎo)致數(shù)據(jù)不同步的情況。
public class MyObject {
    private String username = "l";
    private String password = "ll";
    public void setValue(String username,String password){
        this.username = username;
        if (Thread.currentThread().getName().equals("a")){
            System.out.println("停止a線程!");
            Thread.currentThread().suspend();
        }
        this.password = password;
    }
    public void printUsernamePassword(){
        System.out.println(username+"  "+password);
    }
}


public class SuspendTestThread2 extends Thread {
    public static void main(String[] args) throws InterruptedException {
        final MyObject myObject = new MyObject();
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                myObject.setValue("a","aa");
            }
        };
        thread1.setName("a");
        thread1.start();
        Thread.sleep(500);
        Thread thread2 = new Thread(){
            @Override
            public void run(){
                myObject.printUsernamePassword();
            }
        };
        thread2.start();
    }
}


/*result: 
停止a線程!
a  ll
*/

suspend()和resume()方法已經(jīng)廢棄,不建議使用,可以研究。

yield()方法

yield()方法是讓當(dāng)前線程放棄cpu資源,但放棄的時(shí)間不確定,可能剛剛放棄就立刻獲得cpu資源。

public class YieldTestThread extends Thread {
    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0;i < 50000000; i++){
            //Thread.yield();
            count = count + (i+1);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用時(shí):"+(endTime-beginTime)+"毫秒");
    }


    public static void main(String[] args) {
        YieldTestThread thread = new YieldTestThread();
        thread.start();
    }
}


/*result1:(不加yield)
用時(shí):18毫秒
*/

/*result2:(加yield)
用時(shí):3362毫秒
*/

線程優(yōu)先級

線程可以劃分優(yōu)先級,從1-10級,其他會(huì)報(bào)錯(cuò)。
線程的優(yōu)先級具有承繼性。
優(yōu)先級規(guī)則,總是大部分先執(zhí)行優(yōu)先級高的線程。

//線程1
public class PriorityTestThread extends Thread{
    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        long addResult = 0;
        for (int j = 0;j < 10;j++){
            for(int i = 0;i<50000;i++){
                Random random = new Random();
                random.nextInt();
                addResult = addResult+1;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("* * * * * * thread 1 use time="+(endTime - beginTime));
    }
}

//線程2
public class PriorityTestThread1 extends Thread {

    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        long addResult = 0;
        for (int j = 0;j < 10;j++){
            for(int i = 0;i<50000;i++){
                Random random = new Random();
                random.nextInt();
                addResult = addResult+1;
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("* * * * * * thread 2 use time="+(endTime - beginTime));
    }
}

public class Run {
    public static void main(String[] args) {
        for (int i = 0;i < 100;i++){
            PriorityTestThread thread1 = new PriorityTestThread();
            thread1.setPriority(10);
            thread1.start();
            PriorityTestThread1 thread2 = new PriorityTestThread1();
            thread2.setPriority(1);
            thread2.start();
        }
    }

}

/*result:
... ...
* * * * * * thread 1 use time=6197
* * * * * * thread 1 use time=6207
* * * * * * thread 1 use time=6252
* * * * * * thread 1 use time=6270
* * * * * * thread 2 use time=6870
* * * * * * thread 2 use time=6524
* * * * * * thread 2 use time=7036
* * * * * * thread 1 use time=7522
* * * * * * thread 1 use time=6448
* * * * * * thread 2 use time=7035
* * * * * * thread 2 use time=7223
* * * * * * thread 2 use time=7025
* * * * * * thread 1 use time=7776
* * * * * * thread 1 use time=6747
* * * * * * thread 2 use time=7261
* * * * * * thread 1 use time=7939
... ...
*/

優(yōu)先級高的不是一定先執(zhí)行。

守護(hù)線程

守護(hù)線程是一種特殊線程,特性有“陪伴”的含義,當(dāng)進(jìn)程中不存在非守護(hù)進(jìn)程時(shí),守護(hù)進(jìn)程就自動(dòng)銷毀了。典型的守護(hù)進(jìn)程就是垃圾回收線程(垃圾回收器 GC)

public class DaemonTestThread extends Thread {
    private int i = 0;

    @Override
    public void run() {
        try {
            while (true){
                i++;
                System.out.println("i = "+ i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        try {
            DaemonTestThread thread = new DaemonTestThread();
            thread.setDaemon(true);
            thread.start();
            Thread.sleep(5000);
            System.out.println("我離開Thread對象也不再打印了,也就是停止了!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
//線程thread為主線程的守護(hù)進(jìn)程,主線程停止守護(hù)進(jìn)程也結(jié)束。

/*result:
i = 1
i = 2
i = 3
i = 4
i = 5
我離開Thread對象也不再打印了,也就是停止了!
*/

對象及變量的并發(fā)訪問

synchronzed同步方法

"非線程安全"會(huì)在多個(gè)線程對同一個(gè)對象中的實(shí)例變量進(jìn)行并發(fā)訪問時(shí)發(fā)生產(chǎn)生"臟讀",也就是取到的數(shù)據(jù)其實(shí)是被更改過的。而線程安全就是以獲得的實(shí)例變量的值是經(jīng)過同步處理的,不會(huì)出現(xiàn)臟讀現(xiàn)象。

方法內(nèi)數(shù)據(jù)為線程安全

"非線程安全"問題存在于"實(shí)例變量"中,如果是方法內(nèi)部私有變量則不存在"非線程安全問題"。

public class HasSelfPrivateNum {
    public void addI(String username){
        try {
            int num = 0;
            if (username.equals("a")){
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("b set over!");
                Thread.sleep(2000);
            }
            System.out.println(username + " num = "+num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class ThreadA extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef){
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }
}



public class ThreadB extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef){
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }

}



public class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        ThreadA threadA = new ThreadA(numRef);
        threadA.start();
        ThreadB threadB = new ThreadB(numRef);
        threadB.start();
    }
}



/*result:
a set over!
b set over!
a num = 100
b num = 200

*/

實(shí)例變量非線程安全

若多個(gè)線程訪問一個(gè)對象實(shí)例中的實(shí)例變量。則可能發(fā)生“非線程安全”問題。
用線程訪問的對象中如果有多個(gè)實(shí)例變量,則運(yùn)行的結(jié)果有可能出現(xiàn)交叉的情況。
如果對象僅有一個(gè)實(shí)例變量,則有可能出現(xiàn)覆蓋的情況。

public class HasSelfPrivateNum{
    private int num = 0;
    
    //addI()方法前加上synchronized關(guān)鍵字,避免“非線程安全問題”
   synchronized public void addI(String username){
        try {
            if (username.equals("a")){
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("b set over!");
                Thread.sleep(2000);
            }
            System.out.println(username + " num = "+num);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class ThreadA extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRf){
        super();
        this.numRef = numRf;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }
}


public class ThreadB extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRf){
        super();
        this.numRef = numRf;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }

}


public class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRef = new HasSelfPrivateNum();
        ThreadA threadA = new ThreadA(numRef);
        threadA.start();
        ThreadB threadB = new ThreadB(numRef);
        threadB.start();
    }
}


/*不加synchronized關(guān)鍵字:
a set over!
b set over!
b num = 200
a num = 200
*/


/*加synchronized關(guān)鍵字:
a set over!
a num = 100
b set over!
b num = 200
*/

多個(gè)對象多個(gè)鎖

synchronized關(guān)鍵字取得的鎖都對象鎖,哪個(gè)線程先執(zhí)行帶有synchronized關(guān)鍵字的方法就先獲得對象鎖,其他線程只能依次等待執(zhí)行完成。

public class LockTestObject {
   synchronized public void methodA(){
       try {
           System.out.println("Begin methodA threadName = "+Thread.currentThread().getName());
           Thread.sleep(5000);
           System.out.println("methodA end! endTime = "+System.currentTimeMillis());
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }

  synchronized public void methodB(){
       try {
           System.out.println("Begin methodB threadName = "+Thread.currentThread().getName());
           Thread.sleep(5000);
           System.out.println("methodB end! endTime = "+System.currentTimeMillis());
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}



public class LockThreadA extends Thread {
   private LockTestObject object;
   public LockThreadA(LockTestObject object){
       super();
       this.object = object;
   }

   @Override
   public void run() {
       super.run();
       object.methodA();
   }
}



public class LockThreadB extends Thread {
   private LockTestObject object;
   public LockThreadB(LockTestObject object){
       super();
       this.object = object;
   }

   @Override
   public void run() {
       super.run();
       object.methodB();
   }
}


public class Run {
   public static void main(String[] args) {
       LockTestObject object = new LockTestObject();
       LockThreadA threadA = new LockThreadA(object);
       threadA.setName("A");
       LockThreadB threadB = new LockThreadB(object);
       threadB.setName("B");
       threadA.start();
       threadB.start();
   }
}


/*methodA方法不加synchronized關(guān)鍵字
Begin methodB threadName = B
Begin methodA threadName = A
methodB end! endTime = 1521719664578
methodA end! endTime = 1521719664579
*/

/*methodA方法加上synchronized關(guān)鍵字
Begin methodA threadName = A
methodA end! endTime = 1521719556410
Begin methodB threadName = B
methodB end! endTime = 1521719561411
*/

臟讀

所謂臟讀是在對去實(shí)例變量時(shí)該變量已被其他線程改過,讀出數(shù)據(jù)有誤。

public class PublicVar {
    public String userName = "A";
    public String password = "AA";
    synchronized public void setValue(String userName,String password){
        try {
            this.userName = userName;
            Thread.sleep(5000);
            this.password = password;
            System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void getValue(){
        System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
    }
}


public class DirtyReadTestThread extends Thread{
    private PublicVar publicVar;
    public DirtyReadTestThread(PublicVar publicVar){
        super();
        this.publicVar = publicVar;
    }

    @Override
    public void run() {
        super.run();
        publicVar.setValue("B","BB");
    }
}


public class Run {
    public static void main(String[] args) {
        try {
            PublicVar publicVar = new PublicVar();
            DirtyReadTestThread thread = new DirtyReadTestThread(publicVar);
            thread.start();
            Thread.sleep(200);
            publicVar.getValue();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


/*result:
setValue method thread name = main userName = B password = AA
setValue method thread name = Thread-0 userName = B password = BB
*/

如上所示,main線程出現(xiàn)了臟讀,因?yàn)間etValue()方法不是同步的,只需在getValue前加上synchronized關(guān)鍵字,即可保持?jǐn)?shù)據(jù)同步性。

 synchronized public void getValue(){
        System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password);
    }
    
/*result:
setValue method thread name = Thread-0 userName = B password = BB
setValue method thread name = main userName = B password = BB
*/

當(dāng)線程調(diào)用對象包含的synchronized方法時(shí)獲取了對象的X鎖,但別的線程可以調(diào)用該實(shí)體非synchronized方法。

synchronized 鎖重入

一個(gè)線程多次請求synchronized方法鎖時(shí),可以重復(fù)獲得方法所在的對象實(shí)體的X鎖
鎖重入,即可重復(fù)獲得內(nèi)部鎖。

public class Service {
    synchronized public void service1(){
        System.out.println("service1");
        service2();
    }

    synchronized public void service2(){
        System.out.println("service2");
        service3();
    }
    synchronized public void service3(){
        System.out.println("service3");
    }
}

public class LockReentryTestThread  extends Thread {
    @Override
    public void run() {
        Service service = new Service();
        service.service1();
    }
}


public class Run {
    public static void main(String[] args) {
        LockReentryTestThread thread = new LockReentryTestThread();
        thread.start();
    }
}

/*result:
service1
service2
service3
*/

service類中鎖就重入了,三個(gè)service方法相互調(diào)用。

鎖重入也支持在父子類間的鎖重用。

出現(xiàn)異常,鎖自動(dòng)釋放,其他線程繼續(xù)調(diào)用。

synchronized方法的弊端

導(dǎo)致進(jìn)程等待時(shí)間較長,失去多線程的意義,導(dǎo)致程序響應(yīng)時(shí)間過長。
synchronized同步塊可以解決這個(gè)問題。

synchronized同步代碼塊

當(dāng)兩個(gè)并發(fā)的線程訪問同一個(gè)對象object中的synchronized(this)同步塊時(shí),一段時(shí)間內(nèi)只有一個(gè)線程能訪問并執(zhí)行,另一個(gè)線程必須等待前一個(gè)線程執(zhí)行完畢這個(gè)代碼塊之后,才能執(zhí)行這塊代碼。

使用同步synchronized 代碼塊時(shí),同一個(gè)object的同步代碼塊使用同一個(gè)對象監(jiān)視器,執(zhí)行一個(gè)同步塊時(shí)對象中其他同步塊會(huì)被阻塞。

將任意對象作為對象監(jiān)視器

鎖非this對象的優(yōu)點(diǎn)是:若在一個(gè)類中有很多個(gè)synchronized方法,這時(shí)雖然能實(shí)現(xiàn)同步,但會(huì)收到阻塞,影響運(yùn)行效率;若使用同步代碼塊鎖非this對象,則synchronized(非this)代碼塊中的程序與同步方法是異步的,不與其他鎖this的同步方法爭搶this鎖。則可提高運(yùn)行效率。

靜態(tài)同步synchronized方法與synchronized(class)代碼塊。

關(guān)鍵字還可以作用在static靜態(tài)方法上,是對方法所在的類.class持鎖,而不是對一個(gè)對象上鎖。
synchronized代碼塊也可以對class類上鎖,實(shí)現(xiàn)同步。synchronized(xxx.class)。

數(shù)據(jù)類型String的常量池特性

由于JVM中String數(shù)據(jù)類型的常量池特性 a==b 返回true,所以不使用String對象作為對象監(jiān)視器(對象鎖)。

同步synchronized方法無限等待與解決

synchronized同步方法容易造成死循環(huán),是形成陷入死鎖。同步塊可以解開這個(gè)死鎖問題,死鎖線程依舊跳不出,但其他線程可獲得鎖。

多線程的死鎖

synchronized嵌套代碼塊將帶來死鎖。

進(jìn)入Cmd 輸入 jsp 查找Run的id值 在輸入 jstack -l 19560 可查看程序運(yùn)行死鎖情況

內(nèi)置類與靜態(tài)內(nèi)置類

鎖對象的改變

public class MyService {
    private String lock = "123";
    public void testMethod() {
        try {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + "  begin  " + System.currentTimeMillis());
                lock = "456";
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "  end  " + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class ThreadA extends Thread {
    private MyService service;
    public ThreadA(MyService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.testMethod();
    }
}


public class ThreadB extends Thread{
    private MyService service;
    public ThreadB(MyService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.testMethod();
    }
}


public class Run1 {
    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");

        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadA.start();
        //Thread.sleep(50 );
        threadB.start();
    }
}


/*延遲50毫秒,爭搶兩個(gè)鎖
A  begin  1523325785537
B  begin  1523325785585
A  end  1523325787539
B  end  1523325787585
*/

/*不延遲,爭搶一個(gè)鎖
A  begin  1523325815403
A  end  1523325817403
B  begin  1523325817403
B  end  1523325819404
*/

只要鎖對象不變,即使對象屬性改變依舊同步,線程還是爭搶一個(gè)鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容