成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java 線程通信 線程組 線程異常處理機(jī)制

ivydom / 2210人閱讀

摘要:線程通信傳統(tǒng)的線程通信方法概述方法導(dǎo)致當(dāng)前線程等待,直到其他線程調(diào)用該同步監(jiān)視器的方法或方法來喚醒該線程。運(yùn)行結(jié)果如下線程組和未處理的異常表示線程組,可以對(duì)一批線程進(jìn)行分類管理。對(duì)線程組的控制相當(dāng)于同時(shí)控制這批線程。

線程通信 傳統(tǒng)的線程通信

方法概述:

wait方法:導(dǎo)致當(dāng)前線程等待,直到其他線程調(diào)用該同步監(jiān)視器的notify()方法或notifyAll()方法來喚醒該線程。

wait()方法有三種形式——無時(shí)間參數(shù)的wait()方法(一直等待,直到其他線程通知);
帶毫秒?yún)?shù)的wait()方法、帶毫秒、毫微妙參數(shù)的wait()方法,這2種方法都是等待指定時(shí)間后自動(dòng)蘇醒
調(diào)用wait()方法的當(dāng)前線程會(huì)釋放對(duì)該同步監(jiān)視器的鎖定

notify:喚醒在此同步監(jiān)視器上等待的單個(gè)線程。如果所有線程都在此同步監(jiān)視器上等待,則會(huì)隨機(jī)選擇喚醒其中一個(gè)線程。只有當(dāng)前線程放棄對(duì)該同步監(jiān)視器的鎖定后(用wait()方法),才可以執(zhí)行被喚醒的線程

notifyAll:喚醒在此同步監(jiān)視器上等待的所有線程。只有當(dāng)前線程放棄對(duì)該同步監(jiān)視器的鎖定后,才能執(zhí)行喚醒的線程

這三個(gè)方法屬于Object類,必須由同步監(jiān)視器對(duì)象來調(diào)用,可分成以下兩種情況:

對(duì)于使用synchronize修飾的同步方法,因?yàn)樵擃惖哪J(rèn)實(shí)例(this)就是同步監(jiān)視器,所以可以在同步方法中直接調(diào)用這三個(gè)方法

對(duì)于使用synchronized修改的同步代碼塊,同步監(jiān)視器是synchronized后可括號(hào)中的對(duì)象,所以必須使用括號(hào)中的對(duì)象調(diào)用這3個(gè)方法

public class Account
{
    // 封裝賬戶編號(hào)、賬戶余額的兩個(gè)成員變量
    private String accountNo;
    private double balance;
    // 標(biāo)識(shí)賬戶中是否已有存款的旗標(biāo)
    private boolean flag = false;

    public Account(){}
    // 構(gòu)造器
    public Account(String accountNo , double balance)
    {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo)
    {
        this.accountNo = accountNo;
    }
    public String getAccountNo()
    {
        return this.accountNo;
    }
    // 因此賬戶余額不允許隨便修改,所以只為balance提供getter方法,
    public double getBalance()
    {
        return this.balance;
    }

    public synchronized void draw(double drawAmount)
    {
        try
        {
            // 如果flag為假,表明賬戶中還沒有人存錢進(jìn)去,取錢方法阻塞
            if (!flag)
            {
                wait();
            }
            else
            {
                // 執(zhí)行取錢
                System.out.println(Thread.currentThread().getName()
                    + " 取錢:" +  drawAmount);
                balance -= drawAmount;
                System.out.println("賬戶余額為:" + balance);
                // 將標(biāo)識(shí)賬戶是否已有存款的旗標(biāo)設(shè)為false。
                flag = false;
                // 喚醒其他線程
                notifyAll();
            }
        }
        catch (InterruptedException ex)
        {
            ex.printStackTrace();
        }
    }
    public synchronized void deposit(double depositAmount)
    {
        try
        {
            // 如果flag為真,表明賬戶中已有人存錢進(jìn)去,則存錢方法阻塞
            if (flag)             //①
            {
                wait();
            }
            else
            {
                // 執(zhí)行存款
                System.out.println(Thread.currentThread().getName()
                    + " 存款:" +  depositAmount);
                balance += depositAmount;
                System.out.println("賬戶余額為:" + balance);
                // 將表示賬戶是否已有存款的旗標(biāo)設(shè)為true
                flag = true;
                // 喚醒其他線程
                notifyAll();
            }
        }
        catch (InterruptedException ex)
        {
            ex.printStackTrace();
        }
    }

    // 下面兩個(gè)方法根據(jù)accountNo來重寫hashCode()和equals()方法
    public int hashCode()
    {
        return accountNo.hashCode();
    }
    public boolean equals(Object obj)
    {
        if(this == obj)
            return true;
        if (obj !=null && obj.getClass() == Account.class)
        {
            Account target = (Account)obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}

使用Condition控制線程通信

直接使用Lock對(duì)象來保證同步,則系統(tǒng)中不存在隱式的同步監(jiān)視器,不能使用wait()、notify()、notifyAll()方法進(jìn)行線程通信

當(dāng)使用Lock對(duì)象來保證同步時(shí),Java提供一個(gè)Condition類來保持協(xié)調(diào),使用Condition可以讓那些已經(jīng)得到Lock對(duì)象卻無法繼續(xù)執(zhí)行的線程釋放Lock對(duì)象,Condition對(duì)象也可以喚醒其他處于等待的線程

Condition將同步監(jiān)視器方法(wait、notify、notifyAll)分解成截然不同的對(duì)象,以便通過將這些對(duì)象與Lock對(duì)象組合使用,為每個(gè)對(duì)象提供了多個(gè)等待集(wait-set),這種情況下,Lock替代了同步方法和同步代碼塊,Condition替代同步監(jiān)視器的功能

Condition實(shí)例被綁定在一個(gè)Lock對(duì)象上,要獲得特定的Lock實(shí)例的Condition實(shí)例,調(diào)用Lock對(duì)象的newCondition()即可

Condition類方法介紹:

await():類似于隱式同步監(jiān)視器上的wait方法,導(dǎo)致當(dāng)前程序等待,直到其他線程調(diào)用該Condition的signal()方法和signalAll()方法來喚醒該線程。該await方法有跟多獲取變體:long awaitNanos(long nanosTimeout)、void awaitUninterruptibly()、awaitUntil(Date daadline)等

signal():喚醒在此Lock對(duì)象上等待的單個(gè)線程,如果所有的線程都在該Lock對(duì)象上等待,則會(huì)選擇隨機(jī)喚醒其中一個(gè)線程。只有當(dāng)前線程放棄對(duì)該Lock對(duì)象的鎖定后(使用await()方法),才可以喚醒在執(zhí)行的線程

signalAll():喚醒在此Lock對(duì)象上等待的所有線程。只有當(dāng)前線程放棄對(duì)該Lock對(duì)象的鎖定后,才可以執(zhí)行被喚醒的線程

import java.util.concurrent.*;
import java.util.concurrent.locks.*;
public class Account
{
    // 顯式定義Lock對(duì)象
    private final Lock lock = new ReentrantLock();
    // 獲得指定Lock對(duì)象對(duì)應(yīng)的Condition
    private final Condition cond  = lock.newCondition();
    // 封裝賬戶編號(hào)、賬戶余額的兩個(gè)成員變量
    private String accountNo;
    private double balance;
    // 標(biāo)識(shí)賬戶中是否已有存款的旗標(biāo)
    private boolean flag = false;

    public Account(){}
    // 構(gòu)造器
    public Account(String accountNo , double balance)
    {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // accountNo的setter和getter方法
    public void setAccountNo(String accountNo)
    {
        this.accountNo = accountNo;
    }
    public String getAccountNo()
    {
        return this.accountNo;
    }
    // 因此賬戶余額不允許隨便修改,所以只為balance提供getter方法,
    public double getBalance()
    {
        return this.balance;
    }

    public void draw(double drawAmount)
    {
        // 加鎖
        lock.lock();
        try
        {
            // 如果flag為假,表明賬戶中還沒有人存錢進(jìn)去,取錢方法阻塞
            if (!flag)
            {
                cond.await();
            }
            else
            {
                // 執(zhí)行取錢
                System.out.println(Thread.currentThread().getName() + " 取錢:" +  drawAmount);
                balance -= drawAmount;
                System.out.println("賬戶余額為:" + balance);
                // 將標(biāo)識(shí)賬戶是否已有存款的旗標(biāo)設(shè)為false。
                flag = false;
                // 喚醒其他線程
                cond.signalAll();
            }
        }
        catch (InterruptedException ex)
        {
            ex.printStackTrace();
        }
        // 使用finally塊來釋放鎖
        finally
        {
            lock.unlock();
        }
    }
    public void deposit(double depositAmount)
    {
        lock.lock();
        try
        {
            // 如果flag為真,表明賬戶中已有人存錢進(jìn)去,則存錢方法阻塞
            if (flag)             // ①
            {
                cond.await();
            }
            else
            {
                // 執(zhí)行存款
                System.out.println(Thread.currentThread().getName() + " 存款:" +  depositAmount);
                balance += depositAmount;
                System.out.println("賬戶余額為:" + balance);
                // 將表示賬戶是否已有存款的旗標(biāo)設(shè)為true
                flag = true;
                // 喚醒其他線程
                cond.signalAll();
            }
        }
        catch (InterruptedException ex)
        {
            ex.printStackTrace();
        }
        // 使用finally塊來釋放鎖
        finally
        {
            lock.unlock();
        }
    }

    // 下面兩個(gè)方法根據(jù)accountNo來重寫hashCode()和equals()方法
    public int hashCode()
    {
        return accountNo.hashCode();
    }
    public boolean equals(Object obj)
    {
        if(this == obj)
            return true;
        if (obj !=null
            && obj.getClass() == Account.class)
        {
            Account target = (Account)obj;
            return target.getAccountNo().equals(accountNo);
        }
        return false;
    }
}
使用阻塞隊(duì)列(BlockingQueue)控制線程通信

BlockingQueue具有一個(gè)特征:當(dāng)生產(chǎn)者線程試圖向BlockingQueue中放入元素時(shí),如果該隊(duì)列已滿,則線程被阻塞;但消費(fèi)者線程試圖從BlockingQueue中取出元素時(shí),如果隊(duì)列已空,則該線程阻塞

程序的兩個(gè)線程通過交替向BlockingQueue中放入元素、取出元素,即可很好地控制線程的通信

BlockingQueue提供如下兩個(gè)支持阻塞的方法:

put(E e):嘗試把E元素放入BlockingQueue中,如果該隊(duì)列的元素已滿,則阻塞該線程

take():嘗試從BlockingQueue的頭部取出元素,如果該隊(duì)列的元素已空,則阻塞該線程

BlockingQueue繼承了Queue接口,當(dāng)然也可以使用Queue接口中的方法,這些方法歸納起來可以分為如下三組:

在隊(duì)列尾部插入元素,包括add(E e)、offer(E e)和put(E e)方法,當(dāng)該隊(duì)列已滿時(shí),這三個(gè)方法分別會(huì)拋出異常、返回false、阻塞隊(duì)列

在隊(duì)列頭部刪除并返回刪除的元素。包括remove()、poll()和take()方法,當(dāng)該隊(duì)列已空時(shí),這三個(gè)方法分別會(huì)拋出異常、返回false、阻塞隊(duì)列

在隊(duì)列頭部取出但不刪除元素。包括element()和peek()方法,當(dāng)隊(duì)列已空時(shí),這兩個(gè)方法分別拋出異常、返回false

- 拋出異常 不同返回值 阻塞線程 指定超時(shí)時(shí)差
隊(duì)尾插入元素 add(e) offer(e) put(e) offer(e, time, unit)
隊(duì)頭刪除元素 remove() poll() take() poll(time, unit)
獲取、不刪除元素 element() peek()

BlockingQueue的5個(gè)實(shí)現(xiàn)類:

ArrayBlockingQueue:基于數(shù)組實(shí)現(xiàn)的BlockingQueue隊(duì)列

LinkedBlockingQueue:基于鏈表實(shí)現(xiàn)的BlockingQueue隊(duì)列

PriorityBlockingQueue:它并不是標(biāo)準(zhǔn)的阻塞隊(duì)列,當(dāng)調(diào)用remove()、poll()、take()等方法取出元素時(shí),并不是取出隊(duì)列中存在時(shí)間最長的元素,而是隊(duì)列中最小的元素。判斷元素的大小可根據(jù)元素(實(shí)現(xiàn)Comparable接口)的本身大小來自然排序,也可以使用Comparator進(jìn)行定制排序

SynchronousQueue:同步隊(duì)列,對(duì)該隊(duì)列的存、取操作必須交替進(jìn)行

DelayQueue:它是一個(gè)特殊的BlockingQueue,底層基于PriorityBlockingQueue實(shí)現(xiàn),DelayQueue要求集合元素都實(shí)現(xiàn)Delay接口(該接口里只有一個(gè)long getDelay()方法),DelayQueue根據(jù)集合元素的getDelay()方法的返回值進(jìn)行排序

import java.util.concurrent.*;

public class BlockingQueueTest
{
    public static void main(String[] args) throws Exception
    {
        // 定義一個(gè)長度為2的阻塞隊(duì)列
        BlockingQueue bq = new ArrayBlockingQueue<>(2);
        bq.put("Java"); // 與bq.add("Java"、bq.offer("Java")相同
        bq.put("Java"); // 與bq.add("Java"、bq.offer("Java")相同
        bq.put("Java"); // ① 阻塞線程。
    }
}

利用BlockingQueue實(shí)現(xiàn)線程通信

import java.util.concurrent.*;

class Producer extends Thread
{
    private BlockingQueue bq;
    public Producer(BlockingQueue bq)
    {
        this.bq = bq;
    }
    public void run()
    {
        String[] strArr = new String[]
        {
            "Java",
            "Struts",
            "Spring"
        };
        for (int i = 0 ; i < 999999999 ; i++ )
        {
            System.out.println(getName() + "生產(chǎn)者準(zhǔn)備生產(chǎn)集合元素!");
            try
            {
                Thread.sleep(200);
                // 嘗試放入元素,如果隊(duì)列已滿,線程被阻塞
                bq.put(strArr[i % 3]);
            }
            catch (Exception ex){ex.printStackTrace();}
            System.out.println(getName() + "生產(chǎn)完成:" + bq);
        }
    }
}
class Consumer extends Thread
{
    private BlockingQueue bq;
    public Consumer(BlockingQueue bq)
    {
        this.bq = bq;
    }
    public void run()
    {
        while(true)
        {
            System.out.println(getName() + "消費(fèi)者準(zhǔn)備消費(fèi)集合元素!");
            try
            {
                Thread.sleep(200);
                // 嘗試取出元素,如果隊(duì)列已空,線程被阻塞
                bq.take();
            }
            catch (Exception ex){ex.printStackTrace();}
            System.out.println(getName() + "消費(fèi)完成:" + bq);
        }
    }
}
public class BlockingQueueTest2
{
    public static void main(String[] args)
    {
        // 創(chuàng)建一個(gè)容量為1的BlockingQueue
        BlockingQueue bq = new ArrayBlockingQueue<>(1);
        // 啟動(dòng)3條生產(chǎn)者線程
        new Producer(bq).start();
        new Producer(bq).start();
        new Producer(bq).start();
        // 啟動(dòng)一條消費(fèi)者線程
        new Consumer(bq).start();
    }
}

程序啟動(dòng)3個(gè)生產(chǎn)者線程向BlockingQueue集合放入元素,啟動(dòng)1個(gè)消費(fèi)者線程從BlockingQueue集合取出元素。本程序的BlockingQueue集合容量為1,因此3個(gè)生產(chǎn)者線程無法連續(xù)放入元素,必須等待消費(fèi)者線程取出一個(gè)元素后,3個(gè)生產(chǎn)者線程的其中一個(gè)才能放入元素。運(yùn)行結(jié)果如下:

線程組和未處理的異常

ThreadGroup表示線程組,可以對(duì)一批線程進(jìn)行分類管理。對(duì)線程組的控制相當(dāng)于同時(shí)控制這批線程。默認(rèn)情況下,子線程和創(chuàng)建它的父線程屬于同一個(gè)線程組

一旦某個(gè)線程加入了指定線程組之后,該線程將一直屬于該線程組,直到該線程死亡,線程運(yùn)行中不能改變它所屬的線程組

Thread類提供了如下幾個(gè)構(gòu)造器來設(shè)置新創(chuàng)建的線程屬于哪個(gè)線程組:

Thread(ThreadGroup group, Runnable target):以target的run()方法作為線程執(zhí)行體創(chuàng)建新線程,屬于group線程組

Thread(ThreadGroup group, Runnable target, String name):以target的run()方法作為線程執(zhí)行體創(chuàng)建新線程,屬于group線程組,且線程名為name

Thread(ThreadGroup group, String name):創(chuàng)建新線程,新線程名為name,屬于group線程組

因?yàn)橹型静豢筛淖兙€程所屬的線程組,所以Thread類沒有setThreadGroup()方法,但提供getThreadGroup()方法來返回該線程所屬的線程組,返回值是ThreadGroup對(duì)象

ThreadGroup類的兩個(gè)構(gòu)造器:

ThreadGroup(String name):以指定的線程組名字來創(chuàng)建新的線程組

ThreadGroup(ThreadGroup parent, String name):以指定的名字、指定的父線程組創(chuàng)建新的線程組

ThreadGroup類操作整個(gè)線程組里的所有線程的幾個(gè)常用方法:

int activeCount():返回此線程組中活動(dòng)線程的數(shù)目

interrupt():中斷此線程組中的所有線程

isDaemon():判斷該線程組是否是后臺(tái)線程組

setDaemon(boolean daemon):把該線程組設(shè)置成后臺(tái)線程組。后臺(tái)線程組具有一個(gè)特征,當(dāng)后臺(tái)線程組的最后一個(gè)線程執(zhí)行結(jié)束或最后一個(gè)線程被銷毀后,后臺(tái)線程組將自動(dòng)銷毀

setMaxPriority(int pri):設(shè)置線程組的最高優(yōu)先級(jí)

class MyThread extends Thread
{
    // 提供指定線程名的構(gòu)造器
    public MyThread(String name)
    {
        super(name);
    }
    // 提供指定線程名、線程組的構(gòu)造器
    public MyThread(ThreadGroup group, String name)
    {
        super(group, name);
    }
    public void run()
    {
        for (int i = 0; i < 20 ; i++ )
        {
            System.out.println(getName() + " 線程的i變量" + i);
        }
    }
}
public class ThreadGroupTest
{
    public static void main(String[] args)
    {
        // 獲取主線程所在的線程組,這是所有線程默認(rèn)的線程組
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        System.out.println("主線程組的名字:" + mainGroup.getName());
        System.out.println("主線程組是否是后臺(tái)線程組:" + mainGroup.isDaemon());
        new MyThread("主線程組的線程").start();
        ThreadGroup tg = new ThreadGroup("新線程組");
        tg.setDaemon(true);
        System.out.println("tg線程組是否是后臺(tái)線程組:" + tg.isDaemon());
        MyThread tt = new MyThread(tg, "tg組的線程甲");
        tt.start();
        new MyThread(tg, "tg組的線程乙").start();
    }
}

void uncaughtException(Thread t, Throwable e):該方法可以處理該線程組內(nèi)的任意線程所拋出的未處理異常

JVM在結(jié)束線程前會(huì)自動(dòng)查找是否有對(duì)應(yīng)的Thread.UncaughtExceptionHandler對(duì)象(該類為是Thread的一個(gè)靜態(tài)內(nèi)部接口),如果找到該處理器對(duì)象,會(huì)調(diào)用該對(duì)象的uncaughtException(Thread t, Throwable e)來處理該異常。t:表示出現(xiàn)異常的線程,e表示該線程拋出的異常

Thread類提供兩個(gè)方法設(shè)置異常處理器:

static setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHanlder eh):為該線程類的所有線程實(shí)例設(shè)置默認(rèn)的異常處理器

static setUncaughtExceptionHandler(Thread.UncaughtExceptionHanlder eh):為指定的線程實(shí)例設(shè)置異常處理器

線程組處理異常的默認(rèn)流程:

如果該線程組有父線程組,則調(diào)用父線程組的uncaughtException()來處理異常

如果該線程對(duì)象所屬線程類有有默認(rèn)異常處理器(由setDefaultUncaughtExceptionHandler()方法設(shè)置的異常處理器),那么就調(diào)用該異常處理器來處理該異常

如果該異常對(duì)象是ThreadDeath的對(duì)象,則不做任何處理;否則,將異常跟蹤棧的信息打印到System.err錯(cuò)誤輸出流,并結(jié)束該線程

class MyExHandler implements Thread.UncaughtExceptionHandler
{
    // 實(shí)現(xiàn)uncaughtException方法,該方法將處理線程的未處理異常
    public void uncaughtException(Thread t, Throwable e)
    {
        System.out.println(t + " 線程出現(xiàn)了異常:" + e);
    }
}
public class ExHandler
{
    public static void main(String[] args)
    {
        // 設(shè)置主線程的異常處理器
        Thread.currentThread().setUncaughtExceptionHandler(new MyExHandler());
        int a = 23 / 0;     // ①
        System.out.println("程序正常結(jié)束!");
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66717.html

相關(guān)文章

  • Java線程核心技術(shù)梳理(附源碼)

    摘要:本文對(duì)多線程基礎(chǔ)知識(shí)進(jìn)行梳理,主要包括多線程的基本使用,對(duì)象及變量的并發(fā)訪問,線程間通信,的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。源碼采用構(gòu)建,多線程這部分源碼位于模塊中。通知可能等待該對(duì)象的對(duì)象鎖的其他線程。 本文對(duì)多線程基礎(chǔ)知識(shí)進(jìn)行梳理,主要包括多線程的基本使用,對(duì)象及變量的并發(fā)訪問,線程間通信,lock的使用,定時(shí)器,單例模式,以及線程狀態(tài)與線程組。 寫在前面 花了一周時(shí)...

    Winer 評(píng)論0 收藏0
  • Java線程并發(fā)編程面試筆錄一覽

    摘要:創(chuàng)建線程的方式方式一將類聲明為的子類。將該線程標(biāo)記為守護(hù)線程或用戶線程。其中方法隱含的線程為父線程?;謴?fù)線程,已過時(shí)。等待該線程銷毀終止。更多的使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線 知識(shí)體系圖: showImg(https://segmentfault.com/img/bVbef6v?w=1280&h=960); 1、線程是什么? 線程是進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)。 2、創(chuàng)建線...

    bitkylin 評(píng)論0 收藏0
  • 并發(fā)編程導(dǎo)論

    摘要:并發(fā)編程導(dǎo)論是對(duì)于分布式計(jì)算并發(fā)編程系列的總結(jié)與歸納。并發(fā)編程導(dǎo)論隨著硬件性能的迅猛發(fā)展與大數(shù)據(jù)時(shí)代的來臨,并發(fā)編程日益成為編程中不可忽略的重要組成部分。并發(fā)編程復(fù)興的主要驅(qū)動(dòng)力來自于所謂的多核危機(jī)。 并發(fā)編程導(dǎo)論是對(duì)于分布式計(jì)算-并發(fā)編程 https://url.wx-coder.cn/Yagu8 系列的總結(jié)與歸納。歡迎關(guān)注公眾號(hào):某熊的技術(shù)之路。 showImg(https://...

    GeekQiaQia 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<