黑馬程序員-多線程

-------android培訓(xùn)java培訓(xùn)期待與您交流!----------

概述
  • 進(jìn)程:是一個(gè)正在執(zhí)行中的程序,每一個(gè)進(jìn)程執(zhí)行都有一個(gè)執(zhí)行的順序狡门。該順序是一個(gè)執(zhí)行路徑陷寝,或者叫一個(gè)控制單元。一個(gè)進(jìn)程中至少有一個(gè)線程其馏。

  • 線程:進(jìn)程中一個(gè)獨(dú)立的控制單元凤跑,線程控制著進(jìn)程的執(zhí)行。

  • 多線程的意義:提高cpu的使用率叛复。從而提高了應(yīng)用程序的使用率仔引。

線程和線程共享"堆內(nèi)存和方法區(qū)內(nèi)存",棧是獨(dú)立的褐奥,一個(gè)線程一個(gè)棧

Java程序的運(yùn)行原理:java命令會(huì)啟動(dòng)java虛擬機(jī)咖耘,啟動(dòng)jvm等于啟動(dòng)了一個(gè)應(yīng)用程序,表示啟動(dòng)了一個(gè)進(jìn)程撬码。該進(jìn)程會(huì)自動(dòng)啟動(dòng)一個(gè)"主線程"儿倒,然后主線程去調(diào)用某個(gè)類的main方法。所以main方法運(yùn)行在主線程中呜笑。

創(chuàng)建線程
  • JAVA已經(jīng)提供了對(duì)線程事物的描述的類即Thread類夫否。有兩種方式創(chuàng)建線程。

第一種方式:繼承Thread類蹈垢;復(fù)寫Thread類中的run方法(將自定義的代碼存在run方法慷吊,讓線程調(diào)用);調(diào)用線程的start方法曹抬,啟動(dòng)線程,調(diào)用run方法急鳄。

package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/8.
 */
public class ThreadDemo{
    public static void main(String[] args) {
        //創(chuàng)建線程任務(wù)對(duì)象
        ThreadCreate td = new ThreadCreate();
        //啟動(dòng)線程谤民,而不是直接調(diào)用run方法。線程的真真的執(zhí)行是由java線程調(diào)度機(jī)制完成的疾宏。
        td.start();

        for(int x = 0; x < 1000; x++)
        {
            System.out.println("Demo " + x);
        }
    }
}

//創(chuàng)建需要執(zhí)行人物的類张足,繼承之Thread類
class ThreadCreate extends Thread
{
    //復(fù)寫Thread類中的run方法。定義自己的內(nèi)容
    public void run(){
        for(int x = 0; x < 100; x++){
            System.out.println("Heall word! " + x);
        }
    }
}
run和start的特點(diǎn)
  • run:僅僅是對(duì)象調(diào)用方法坎藐,并沒有運(yùn)行線程(Thread類的run方法用于存儲(chǔ)線程要運(yùn)行的代碼)为牍。
  • start:開啟線程并執(zhí)行該線程的run方法哼绑。

練習(xí)

package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/8.
 * 創(chuàng)建兩個(gè)線程與main線程交替運(yùn)行
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        //創(chuàng)建兩個(gè)線程對(duì)象
        ThreadCreate1 td1 = new ThreadCreate1("線程一");
        ThreadCreate1 td2 = new ThreadCreate1("線程二");
        //啟動(dòng)兩個(gè)線程
        td1.start();
        td2.start();

        for(int x =0; x < 60; x++)
        {
            System.out.println("main run..." + x);
        }
    }
}

//創(chuàng)建執(zhí)行任務(wù)的線程繼承thread類
class ThreadCreate1 extends Thread
{
    private String name;
    ThreadCreate1(String name)
    {
        this.name = name;
    }
    //復(fù)寫run方法
    public void run(){
        for(int x = 0; x < 60; x++)
        {
            System.out.println(name + "run..." + x);
        }
    }
}
多線程聲明周期
  • 如圖:


    Thread.png

新建:用new語(yǔ)句創(chuàng)建完成
就緒:執(zhí)行start后
運(yùn)行:占用cpu時(shí)間
阻塞:執(zhí)行了wait語(yǔ)句、執(zhí)行了sleep語(yǔ)句和等待某個(gè)對(duì)象鎖碉咆,等待輸入的場(chǎng)合
消亡:退出run()方法

獲取多線程對(duì)象及名稱
  • 線程都有自己的默認(rèn)名稱:Thread-編號(hào)該編號(hào)從0開始抖韩。

線程的幾個(gè)方法:
1.static Thread currentThread():獲取當(dāng)前線程對(duì)象。
2.getName():獲取線程的名稱疫铜。
3.設(shè)置線程名稱:setName或者構(gòu)造函數(shù)茂浮。

package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/8.
 */
public class ThreadDemo1 {
    public static void main(String[] args) {
        //創(chuàng)建兩個(gè)線程對(duì)象
        ThreadCreate1 td1 = new ThreadCreate1("線程一");
        ThreadCreate1 td2 = new ThreadCreate1("線程二");
        //啟動(dòng)兩個(gè)線程
        td1.start();
        td2.start();
    }
}

//創(chuàng)建執(zhí)行任務(wù)的線程繼承thread類
class ThreadCreate1 extends Thread
{
    ThreadCreate1(String name)
    {
        //給線程命名
        super(name);
    }
    //復(fù)寫run方法
    public void run(){
        for(int x = 0; x < 60; x++)
        {
            //Thread.currentThread()通過此靜態(tài)方法獲取當(dāng)前線程對(duì)象
            System.out.println(Thread.currentThread().getName() + "run..." + x);
        }
    }
}
創(chuàng)建線程

創(chuàng)建線程的第二種方式:實(shí)現(xiàn)Runnable接口。
步驟:定義類實(shí)現(xiàn)Runnable接口壳咕;覆蓋Runnable接口中的run方法席揽;通過Thread類建立線程對(duì)象;將Runable接口的子類對(duì)象作為實(shí)際參數(shù)傳遞給Thread類的構(gòu)造函數(shù)谓厘;調(diào)用Thread類的start方法開啟線程并調(diào)用Runnable接口子類的run方法幌羞。

package com.sergio.thread;

import sun.security.krb5.internal.Ticket;

/**
 * Created by Sergio on 2014/12/9.
 */
public class RunnableThreadDemo {
    public static void main(String[] args) {
        //創(chuàng)建賣票類實(shí)例對(duì)象
        RunnableThreadCreate t = new RunnableThreadCreate();
        //將通過Thread類建立線程對(duì)象;將Runnable接口的子類對(duì)象作為實(shí)際參數(shù)傳遞給Thread類的構(gòu)造函數(shù)
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        //啟動(dòng)線程
        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

//實(shí)現(xiàn)Runnable接口竟稳,模擬賣票功能類
class RunnableThreadCreate implements Runnable
{
    private int ticket = 100;
    @Override
    public void run() {
        while(true)
        {
            if(ticket > 0)
            {
                System.out.println(Thread.currentThread().getName() + "售出票號(hào)" + ticket--);
            }
        }
    }
}

實(shí)現(xiàn)方式和繼承方式有什么區(qū)別:
實(shí)現(xiàn)方式的好處:避免了單繼承的局限性新翎,在定義線程時(shí),建議使用實(shí)現(xiàn)方式住练。
區(qū)別:
繼承Thread:線程代碼存放Thread子類run方法中地啰。
實(shí)現(xiàn)Runnable:線程代碼存在接口的子類的run方法

多線程安全

先來(lái)看一個(gè)例子:

package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/9.
 */
public class TreadSafeDemo {
    public static void main(String[] args) {
        //
        ThreadSafeCreateDemo ts = new ThreadSafeCreateDemo();
        Thread t1 = new Thread(ts, "t1");
        t1.start();

        Thread t2 = new Thread(ts, "t2");
        t2.start();
    }
}

class ThreadSafeCreateDemo implements Runnable
{
    //成員變量s,作為累加變量
    private int s = 0;

    @Override
    public void run()
    {
        //計(jì)算1-10的相加合
        for(int i = 0; i < 10;i++)
        {
            s += i;
        }
        System.out.println(Thread.currentThread().getName() + ", s = " + s);
    }
}

打印的結(jié)果為:

t1, s = 45
t2, s = 90

這是為何讲逛?這是因?yàn)楣蚕砹送粋€(gè)對(duì)象的的成員變量s亏吝,兩個(gè)線程同時(shí)對(duì)其進(jìn)行操作,所以產(chǎn)生了上述為題盏混。這個(gè)結(jié)果造成的現(xiàn)象稱為線程不安全問題蔚鸥。

解決方法:對(duì)多條線程操作共享數(shù)據(jù)的語(yǔ)句,只能讓一個(gè)線程都執(zhí)行完许赃,在執(zhí)行過程過中止喷,其他線程不可以參與執(zhí)行。

Java對(duì)于多線程的安全問題提供了解決方式:同步代碼塊

synchronized(對(duì)象)
{
       需要被同步的代碼
}

對(duì)于上述的例子修改如下:

package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/9.
 */
public class TreadSafeDemo {
    public static void main(String[] args) {
        ThreadSafeCreateDemo ts = new ThreadSafeCreateDemo();
        Thread t1 = new Thread(ts, "t1");
        t1.start();

        Thread t2 = new Thread(ts, "t2");
        t2.start();
    }
}

class ThreadSafeCreateDemo implements Runnable
{
    //成員變量s混聊,作為累加變量
    private int s = 0;

    //增加關(guān)鍵字synchronized來(lái)給對(duì)象加鎖使代碼同步安全問題得到解決弹谁,但是會(huì)帶來(lái)效率變慢問題
    @Override
    public synchronized void run()
    {
        //計(jì)算1-10的相加合
        synchronized (this){
            for(int i = 0; i < 10;i++)
            {
                s += i;
            }
        }
        System.out.println(Thread.currentThread().getName() + ", s = " + s);
        s = 0;
    }
}

synchronized同步代碼塊如同給對(duì)象加鎖,持有鎖的線程可以在同步中執(zhí)行句喜,沒有持有鎖的線程即使獲取了cpu的執(zhí)行權(quán)预愤,也進(jìn)不到同步代碼塊中,因?yàn)闆]有獲取鎖咳胃。

同步的前提:

  1. 必須要有兩個(gè)或者兩個(gè)以上的線程
  2. 必須是多個(gè)線程使用同一個(gè)鎖
    必須保證同步中只能有一個(gè)線程在運(yùn)行

好處和弊端:

  1. 好處:解決了多線程的安全問題
  2. 弊端:多個(gè)線程需要判斷鎖植康,較為消耗資源。

同步有兩種表示:同步代碼塊和同步函數(shù)展懈。

如何找同步:

  1. 明確哪些代碼是多線程運(yùn)行代碼销睁。
  2. 明確共享數(shù)據(jù)赊级。
  3. 明確多線程運(yùn)行代碼中那些語(yǔ)句是操作共享數(shù)據(jù)的唯袄。

同步函數(shù)例子:

package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/9.
 * 兩個(gè)客戶同時(shí)往一個(gè)賬戶中存錢,每次100聊倔,分別分三次寸
 */
public class ThreadBankDemo {
    public static void main(String[] args) {
        Cus c = new Cus();
        Thread t1 = new Thread(c, "t1");
        t1.start();

        Thread t2 = new Thread(c, "t2");
        t2.start();

    }
}

//創(chuàng)建銀行類
class Bank
{
    //存錢的成員變量
    private int sum;
    //同步代碼函數(shù)茅诱。存錢函數(shù)
    public synchronized void add(int n)
    {
        sum = sum + n;
        System.out.println(sum);
    }
}

//客戶存錢函數(shù)類實(shí)現(xiàn)多線程接口
class Cus implements Runnable
{
    //創(chuàng)建銀行類對(duì)象
    Bank b = new Bank();
    //需要多線程執(zhí)行的代碼
    @Override
    public void run() {
        for(int x = 0; x < 3; x++)
        {
            //調(diào)用存錢的方法
            b.add(100);
        }
    }
}
同步函數(shù)鎖
  • 函數(shù)需要被對(duì)象調(diào)用,函數(shù)都有一個(gè)所屬對(duì)象引用檩赢,就是this吕嘀。所以同步函數(shù)使用的鎖是this。
    public void run()
    {
        //this當(dāng)前運(yùn)行的對(duì)象引用
        synchronized (this){
            //計(jì)算1-10的相加合
            for(int i = 0; i < 10;i++)
            {
                s += i;
            }
            System.out.println(Thread.currentThread().getName() + ", s = " + s);
            s = 0;
        }
    }
靜態(tài)同步函數(shù)
  • 靜態(tài)方法中不可以定義this贞瞒,沒對(duì)象偶房。
  • 靜態(tài)進(jìn)內(nèi)存時(shí),內(nèi)存中沒有本類對(duì)象军浆,但是一定有該類對(duì)應(yīng)的字節(jié)碼文件對(duì)象棕洋。類名.class 該對(duì)象的類型是Class。
  • 靜態(tài)的同步方法乒融,使用的鎖是該方法所在類的字節(jié)碼文件對(duì)象掰盘。也就是類名.class
    //靜態(tài)的 SinglePattern2實(shí)例對(duì)象變量
    private static SinglePattern2 instance = null;
    //私有構(gòu)造函數(shù)
    private SinglePattern2(){}
    public static SinglePattern2 getInstance()
    {
        if(instance == null)
        {
            //靜態(tài)同步函數(shù)調(diào)用方式類名.class
            synchronized (SinglePattern2.class)
            {
                if(instance == null)
                {
                    instance = new SinglePattern2();
                }
            }
        }
        return instance;
    }
死鎖
  • 概念:同步中嵌套同步但是鎖對(duì)象不同赞季。
package com.sergio.thread;

/**
 * 構(gòu)建鎖的機(jī)制的例子
 * Created by Sergio on 2014/12/10.
 */
public class ThreadDeadLock2 {
    public static void main(String[] args) {
        Test test = new Test(false);
        Test test2 = new Test(true);

        Thread t = new Thread(test);
        Thread t2 = new Thread(test2);

        t.start();
        t2.start();
    }
}

class Test implements Runnable
{
    //設(shè)置運(yùn)行標(biāo)志
    private boolean flag;
    Test(boolean flag)
    {
        this.flag = flag;
    }

    //重寫run方法愧捕,運(yùn)行鎖的機(jī)制。前提:同步中包含同步申钩,但是鎖的對(duì)象引用次绘。
    @Override
    public void run() {
        if(flag)
        {
            //鎖locka對(duì)象的引用
            synchronized (Lock.locka)
            {
                System.out.println("if locka");
                //鎖lock對(duì)象的引用
                synchronized (Lock.lockb)
                {
                    System.out.println("if lockb");
                }
            }
        }
        else
        {
            //鎖lockb對(duì)象的引用
            synchronized (Lock.lockb)
            {
                System.out.println("else lockb");
                //鎖locka對(duì)象的引用
                synchronized (Lock.locka)
                {
                    System.out.println("else locka");
                }
            }
        }
    }
}

//創(chuàng)建鎖類
class Lock
{
    //構(gòu)建兩個(gè)鎖對(duì)象引用
    static Object locka = new Object();
    static Object lockb = new Object();

}
線程間通信
  • 多個(gè)線程在操作同一個(gè)資源,但是操作的動(dòng)作不同撒遣。
package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/11.
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        Res r = new Res();

        Input in = new Input(r);
        Output out = new Output(r);

        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);

        t1.start();
        t2.start();
    }
}

//需要操作的資源類
class Res
{
    String name;
    String sex;
}
//輸入操作線程類
class Input implements Runnable
{
    private Res r;

    Input(Res r)
    {
        this.r = r;
    }

    //存資源運(yùn)行方法
    @Override
    public void run() {

        int x = 0;
        while(true)
        {
            //同步運(yùn)行時(shí)的對(duì)象鎖
            synchronized (r) {
                if (x == 0) {
                    r.name = "mike";
                    r.sex = "man";
                } else {
                    r.name = "麗麗";
                    r.sex = "女";
                }
            }
            //改變運(yùn)行條件標(biāo)志值
            x = (x + 1) % 2;
        }
    }
}

//輸出操作線程類
class Output implements Runnable
{
    private Res r;

    Output(Res r)
    {
        this.r = r;
    }

    //打印資源方式
    @Override
    public void run() {
        while(true)
        {
            //同步運(yùn)行時(shí)的同步鎖
            synchronized (r) {
                System.out.println(r.name + "------" + r.sex);
            }
        }
    }
}
  • 等待喚醒機(jī)制
package com.sergio.thread;

/**
 * Created by Sergio on 2014/12/11.
 */
public class ThreadDemo2 {
    public static void main(String[] args) {
        Res r = new Res();
        
        //啟動(dòng)兩個(gè)線程
        new Thread(new Input(r)).start();
        new Thread(new Output(r)).start();
    }
}


//需要操作的資源類
class Res {
    private String name;
    private String sex;
    //設(shè)置運(yùn)行時(shí)的鎖標(biāo)志
    private boolean flag = false;
    //同步輸入鎖
    public synchronized void set(String name, String sex) {
        if (flag) {
            try {
                //讓同步線程對(duì)象等待邮偎,并撲獲異常。
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.name = name;
        this.sex = sex;
        
        //改變同步鎖運(yùn)行時(shí)的標(biāo)志值
        flag = true;
        //喚醒同步線程池中的鎖义黎。也就是res對(duì)象
        this.notify();
    }

    //同步輸出鎖
    public synchronized void out() {
        if (!flag) {
            try {
                //讓同步線程對(duì)象等待禾进,并撲獲異常。
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name + "--------" + sex);
        //改變同步鎖運(yùn)行時(shí)的標(biāo)志值
        flag = false;
        //喚醒同步線程池中的鎖廉涕。也就是res對(duì)象
        this.notify();
    }
}


//輸入操作線程類
class Input implements Runnable {
    private Res r;

    Input(Res r) {
        this.r = r;
    }

    //存資源運(yùn)行方法
    @Override
    public void run() {

        int x = 0;
        while (true) {
            //交替輸入信息
            if (x == 0) {
                r.set("make", "man");
            } else {
                r.set("麗麗", "女");
            }
            //設(shè)置交替輸入方法運(yùn)行的標(biāo)志
            x = (x + 1) % 2;
        }
    }
}


//輸出操作線程類
class Output implements Runnable {
    private Res r;

    Output(Res r) {
        this.r = r;
    }

    //打印資源方式
    @Override
    public void run() {
        while (true) {
            r.out();
        }
    }
}
  1. wait();notify();notifyall();都是用在同步中泻云,因?yàn)橐獙?duì)持有監(jiān)視器(對(duì)象鎖)的線程操作,而同步中才有鎖火的。
  2. 這些操作方法定義在Object類中壶愤。因?yàn)檫@些方法在操作同步中線程時(shí),都必須要標(biāo)識(shí)他們所操作線程只有的鎖馏鹤,只有同一個(gè)鎖上的被等待線程,可以被同一個(gè)鎖上notify喚醒娇哆。不可以對(duì)不同鎖中的線程進(jìn)行喚醒湃累。也就是說勃救,等待和喚醒必須是同一個(gè)鎖。而鎖可以是任意對(duì)象治力,所以可以被任意對(duì)象調(diào)用的方法定義Object類中蒙秒。
生產(chǎn)者消費(fèi)模式

示例1:(JDK1.5以前方式書寫)

package com.sergio.thread;

/**
 * Created by Sergio on 2015/1/3.
 */
class ProducerConsumerDemo {
    public static void main(String[] args) {
        Resource r = new Resource();

        Producer pro = new Producer(r);
        Consumer con = new Consumer(r);

        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(pro);
        Thread t3 = new Thread(con);
        Thread t4 = new Thread(con);

        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

/*
對(duì)于多個(gè)生產(chǎn)者和消費(fèi)者。
為什么要定義while判斷標(biāo)記宵统。
原因:讓被喚醒的線程再一次判斷標(biāo)記晕讲。


為什么定義notifyAll,
因?yàn)樾枰獑拘褜?duì)方線程马澈。
因?yàn)橹挥胣otify瓢省,容易出現(xiàn)只喚醒本方線程的情況。導(dǎo)致程序中的所有線程都等待痊班。

*/

//定義資源類
class Resource {
    //定義名字變量
    private String name;
    //計(jì)數(shù)基數(shù)
    private int count = 1;
    //線程運(yùn)行標(biāo)記
    private boolean flag = false;

    //設(shè)置生產(chǎn)者模式方法
    public synchronized void set(String name) {
        while (flag)
            try {
                //當(dāng)前線程等待
                this.wait();
            } catch (Exception e) 
            {}
        this.name = name + "--" + count++;

        System.out.println(Thread.currentThread().getName() + "...生產(chǎn)者.." + this.name);
        //更改標(biāo)記值
        flag = true;
        //喚醒剩余全部鎖
        this.notifyAll();
    }

    //設(shè)置消費(fèi)者模式方法
    public synchronized void out() {
        while (!flag)
            try {
                this.wait();
            } catch (Exception e) 
            {}
        System.out.println(Thread.currentThread().getName() + "...消費(fèi)者........." + this.name);
        flag = false;
        this.notifyAll();
    }
}


class Producer implements Runnable {
    private Resource res;

    Producer(Resource res) {
        this.res = res;
    }

    public void run() {
        while (true) {
            res.set("+商品+");
        }
    }
}


class Consumer implements Runnable {
    private Resource res;

    Consumer(Resource res) {
        this.res = res;
    }

    public void run() {
        while (true) {
            res.out();
        }
    }
}

示例2:(JDK1.5版本新特性勤婚。)

  • 將同步Synchronized替換成顯式Lock操作。
  • 將Object中的wait涤伐,notify馒胆,notifyAll替換成了Condition對(duì)象,該對(duì)象可以用Lock鎖來(lái)進(jìn)行獲取凝果。

Lock:替換了Synchronized封裝成了lock祝迂、unlock、newCondition()器净。
Condition:替換了Object型雳、wait、notify掌动、notifyAll封裝成了await()四啰、signal()、signalAll().

package com.sergio.thread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 生產(chǎn)者消費(fèi)者模式
 * Created by Sergio on 2015/1/3.
 */
class ProducerConsumerDemo {
    public static void main(String[] args) {

        Resource r = new Resource();
        Producer2 pro = new Producer2(r);
        Consumer2 con = new Consumer2(r);

        Thread t1 = new Thread(pro);
        Thread t2 = new Thread(pro);
        Thread t3 = new Thread(con);
        Thread t4 = new Thread(con);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}


//定義資源類
class Resource {
    //定義名稱
    private String name;
    //定義計(jì)數(shù)基數(shù)
    private int count = 1;
    //定義線程運(yùn)行標(biāo)志
    private boolean flag = false;

    //定義鎖lock對(duì)象變量
    private Lock lock = new ReentrantLock();
    //定義狀態(tài)condition_pro對(duì)象變量
    private Condition condition_pro = lock.newCondition();
    //定義狀態(tài)condition_con對(duì)象變量
    private Condition condition_con = lock.newCondition();

    //定義生產(chǎn)者的方法
    public void set(String name) throws InterruptedException {
        //獲取當(dāng)前鎖鎖住對(duì)象
        lock.lock();
        try {
            while (flag) {
                //使生產(chǎn)者對(duì)象鎖等待粗恢,在函數(shù)上聲明異常類
                condition_pro.await();
            }
            this.name = name + "--" + count++;

            System.out.println(Thread.currentThread().getName() + "生產(chǎn)者" + this.name);
            flag = true;
            //喚醒消費(fèi)者對(duì)象鎖
            condition_con.signal();

        } finally {
            //釋放鎖的動(dòng)作一定要執(zhí)行柑晒。
            lock.unlock();
        }
    }

    //消費(fèi)者方法
    public synchronized void out() throws InterruptedException {
        lock.lock();
        try {
            while (!flag) {
                //使消費(fèi)者對(duì)象鎖等待。在函數(shù)上聲明異常類
                condition_con.await();
            }
            System.out.println(Thread.currentThread().getName() + "...消費(fèi)者..." + this.name);
            flag = false;
            //喚醒生產(chǎn)者對(duì)象鎖
            condition_pro.signal();
        } finally {
            lock.unlock();
        }
    }
}


//定義生產(chǎn)者
class Producer2 implements Runnable {
    private Resource res;

    Producer2(Resource res) {
        this.res = res;
    }

    public void run() {
        while (true) {
            try {
                res.set("商品");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}


//定義消費(fèi)者
class Consumer2 implements Runnable {
    private Resource res;

    Consumer2(Resource res) {
        this.res = res;
    }

    @Override
    public void run() {
        while (true) {
            try {
                res.out();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
停止線程

stop方法已經(jīng)過時(shí)眷射。

  • 停止方法1:run方法結(jié)束匙赞。開啟多線程運(yùn)行,運(yùn)行代碼通常是循環(huán)結(jié)構(gòu)妖碉。只要控制住循環(huán)涌庭,就可以讓run方法結(jié)束,也就是線程結(jié)束欧宜。
package com.sergio.thread;

/**
 * 利用循環(huán)結(jié)構(gòu)結(jié)束線程
 * Created by Sergio on 2015/1/3.
 */
class StopThread {
    public static void main(String[] args) {
        StopThreadDemo sp = new StopThreadDemo();
        
        Thread t1 = new Thread(sp);
        Thread t2 = new Thread(sp);
        t1.start();
        t2.start();

        //計(jì)數(shù)基數(shù)
        int num = 0;
        while (true)
        {
            if(num++ == 30)
            {
                //num為30后調(diào)用changeFlag方法改變標(biāo)記值
                sp.changeFlag();
                break;
            }
            System.out.println(Thread.currentThread().getName() + "....." + num);
        }

    }
}


class StopThreadDemo implements Runnable
{
    //設(shè)置循環(huán)運(yùn)行標(biāo)記值
    private boolean flag = true;

    @Override
    public void run()
    {
        while(flag)
        {
            //打印當(dāng)然運(yùn)行的線程名字
            System.out.println(Thread.currentThread().getName() + "....run");
        }
    }

    //改變標(biāo)記值為false
    public void changeFlag()
    {
        flag = false;
    }

}
  • 停止方法2:使用interrupt()方法坐榆。結(jié)束線程的凍結(jié)狀態(tài)(sleep、wait冗茸、join方法)席镀,使線程回到運(yùn)行狀態(tài)中來(lái)匹中。也就是靠異常機(jī)制來(lái)結(jié)束線程。
package com.sergio.thread;

/**
 * 打斷線程凍結(jié)狀態(tài):當(dāng)沒有指定的方式讓凍結(jié)的線程恢復(fù)到運(yùn)行狀態(tài)時(shí)豪诲,這時(shí)需要對(duì)凍結(jié)進(jìn)行清除顶捷。
 * 強(qiáng)制讓線程恢復(fù)到運(yùn)行狀態(tài)中來(lái)。改變標(biāo)記讓線程運(yùn)行
 * Created by Sergio on 2015/1/5.
 */
public class StopThreadDemo2 {
    public static void main(String[] args) {
        StopThread2 sp = new StopThread2();

        Thread t1 = new Thread(sp);
        Thread t2 = new Thread(sp);
        t1.start();
        t2.start();

        //計(jì)數(shù)基數(shù)
        int num = 0;
        while (true) {
            if (num++ == 60) {
                //打斷t1屎篱、t2線程的凍結(jié)狀態(tài)服赎,讓線程恢復(fù)到運(yùn)行狀態(tài)
                t1.interrupt();
                t2.interrupt();
                break;
            }
            System.out.println(Thread.currentThread().getName() + "....." + num);
        }
    }
}

class StopThread2 implements Runnable {
    private boolean flag = true;

    @Override
    public synchronized void run() {
        while (flag) {
            try {
                //凍結(jié)狀態(tài)
                wait();
            } catch (InterruptedException e) {
                //異常處理方法
                System.out.println(Thread.currentThread().getName() + "中斷異常");
                flag = false;
            }
            System.out.println(Thread.currentThread().getName() + "...run");
        }
    }
}
守護(hù)線程
  • 線程分類為:用戶線程和守護(hù)線程。守護(hù)線程可以理解為后臺(tái)線程交播,我們看到的都是用戶線程重虑。守護(hù)線程開啟后,同用戶線程共同搶奪cpu執(zhí)行權(quán)堪侯。只是在結(jié)束時(shí)不一樣嚎尤,守護(hù)線程在所有用戶線程結(jié)束后,會(huì)自動(dòng)結(jié)束伍宦。像是守護(hù)依賴于用戶線程芽死。比如說:輸入動(dòng)作線程結(jié)束后輸出動(dòng)作線程自動(dòng)結(jié)束。java的垃圾回收器次洼。

  • 語(yǔ)法: setDaemon(boolean b)关贵。true為開啟。

注意:該方法必須在啟動(dòng)線程前調(diào)用卖毁。

join方法
  • 也就是搶奪cpu的執(zhí)行權(quán)揖曾。可以用來(lái)臨時(shí)加入線程執(zhí)行亥啦。
  • 特點(diǎn):當(dāng)a線程執(zhí)行到了b線程的join方法炭剪,a就會(huì)等待,等b線程都執(zhí)行完翔脱,a才會(huì)從凍結(jié)狀態(tài)恢復(fù)到運(yùn)行狀態(tài)執(zhí)行奴拦。
  • 語(yǔ)法:t1.join();
線程優(yōu)先級(jí)
  • 目前CPU使用權(quán)爭(zhēng)奪有兩種調(diào)度模型:分時(shí)調(diào)度模型和搶占式調(diào)度模型,Java 使用搶占式調(diào)度模型届吁。
    1. 分時(shí)調(diào)度模型:所有線程輪流使用CPU 的使用權(quán)错妖,平均分配每個(gè)線程占用CPU 的時(shí)間片
    2. 搶占式調(diào)度模型:優(yōu)先讓優(yōu)先級(jí)高的線程使用CPU,如果線程的優(yōu)先級(jí)相同疚沐,那么會(huì)隨機(jī)選擇一個(gè)暂氯,優(yōu)先級(jí)高的線程獲取的CPU 時(shí)間片相對(duì)多一些。
  • 線程優(yōu)先級(jí)為了:MAX_PRIORITY(最高級(jí)為10)亮蛔,MIN_PRIORITY(最低級(jí)為1)痴施,NORM_PRIORITY(默認(rèn)及為5)。優(yōu)先級(jí)為:1-10。優(yōu)先級(jí)高的線程只能說明能搶占的cpu執(zhí)行權(quán)較多些晾剖,執(zhí)行的時(shí)間較多些锉矢。
  • 線程啟動(dòng)后不能再次設(shè)置優(yōu)先級(jí)梯嗽。必須在線程啟動(dòng)前設(shè)置優(yōu)先級(jí)齿尽。
package com.sergio.thread;

/**
 * 設(shè)置線程優(yōu)先級(jí)
 * Created by Sergio on 2015/1/5.
 */
public class ThreadPriorityDemo {
    public static void main(String[] args) {
        ThreadPriority tp = new ThreadPriority();

        Thread t1 = new Thread(tp, "t1");
        Thread t2 = new Thread(tp, "t2");
        //設(shè)置t1線程為10等級(jí)
        t1.setPriority(Thread.MAX_PRIORITY);
        t1.start();
        //設(shè)置線程t2為1等級(jí)
        t2.setPriority(Thread.MIN_PRIORITY);
        t2.start();

    }
}

class ThreadPriority implements Runnable
{
    public void run()
    {
        for(int i = 0; i < 100; i++)
        {
            System.out.println(Thread.currentThread().getName() + ",,," + i);
        }
    }
}
yield方法
  • 跟sleep方法類似,只是不能由用戶指定暫停多長(zhǎng)時(shí)間灯节,減緩線程的運(yùn)行頻率循头,并且yield方法只能讓同優(yōu)先級(jí)的線程有執(zhí)行機(jī)會(huì)。
class ThreadPriority implements Runnable
{
    public void run()
    {
        for(int i = 0; i < 100; i++)
        {
            System.out.println(Thread.currentThread().getName() + ",,," + i);
            if(i % 10 == 0)
            {
                System.out.println("--------");
                //暫停當(dāng)前正在執(zhí)行的線程炎疆,并執(zhí)行其他同等優(yōu)先級(jí)的線程
                //當(dāng)i為能被10整除時(shí)卡骂,該線程就會(huì)把CPU時(shí)間讓掉,讓其他或者自己的線程執(zhí)行
                Thread.yield();
            }
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末形入,一起剝皮案震驚了整個(gè)濱河市全跨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌亿遂,老刑警劉巖浓若,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蛇数,居然都是意外死亡挪钓,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門耳舅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)碌上,“玉大人,你說我怎么就攤上這事浦徊×笥瑁” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵盔性,是天一觀的道長(zhǎng)霞丧。 經(jīng)常有香客問我,道長(zhǎng)纯出,這世上最難降的妖魔是什么蚯妇? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮暂筝,結(jié)果婚禮上箩言,老公的妹妹穿的比我還像新娘。我一直安慰自己焕襟,他們只是感情好陨收,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般务漩。 火紅的嫁衣襯著肌膚如雪拄衰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天饵骨,我揣著相機(jī)與錄音翘悉,去河邊找鬼。 笑死居触,一個(gè)胖子當(dāng)著我的面吹牛妖混,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播轮洋,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼制市,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了弊予?” 一聲冷哼從身側(cè)響起祥楣,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎汉柒,沒想到半個(gè)月后误褪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竭翠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年振坚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斋扰。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渡八,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出传货,到底是詐尸還是另有隱情屎鳍,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布问裕,位于F島的核電站逮壁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏粮宛。R本人自食惡果不足惜窥淆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巍杈。 院中可真熱鬧忧饭,春花似錦、人聲如沸筷畦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吼砂,卻和暖如春逆航,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渔肩。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工因俐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赖瞒。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓女揭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親栏饮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文主要講了java中多線程的使用方法磷仰、線程同步袍嬉、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法灶平、概述等伺通。 首先講...
    李欣陽(yáng)閱讀 2,444評(píng)論 1 15
  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類 二實(shí)現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 2,952評(píng)論 1 18
  • 該文章轉(zhuǎn)自:http://blog.csdn.net/evankaka/article/details/44153...
    加來(lái)依藍(lán)閱讀 7,336評(píng)論 3 87
  • 寫在前面的話: 這篇博客是我從這里“轉(zhuǎn)載”的,為什么轉(zhuǎn)載兩個(gè)字加“”呢逢享?因?yàn)檫@絕不是簡(jiǎn)單的復(fù)制粘貼罐监,我花了五六個(gè)小...
    SmartSean閱讀 4,715評(píng)論 12 45
  • 知識(shí)點(diǎn):一. 什么是線程: 進(jìn)程是指運(yùn)行中的應(yīng)用程序,每一個(gè)進(jìn)程都有自己獨(dú)立的內(nèi)存空間瞒爬。一個(gè)應(yīng)用程序可以同時(shí)啟動(dòng)多...
    木有魚丸啦閱讀 645評(píng)論 0 0