Java多線程

線程的狀態

新建(New)、可運行(Runnable)、阻塞(Blocking)、無限期等待(Waiting)、有限期等待(Timed-Waiting)、死亡(Terminated)java

使用線程

  • 實現Callable接口
  • 實現Runnable接口
  • 繼承Thread類

線程機制

用戶線程與守護(Daemon)線程

守護線程時提供通用服務的線程(例如垃圾回收線程),優先級很低
經過Thread對象的setDaemon(boolean)方法設置守護線程與用戶線程,注意此方法必須在線程對象的start方法以前調用,不然會拋出java.lang.IllegalThreadStateException異常
守護線程建立的線程默認是守護線程,用戶線程同理。
守護線程是爲了服務用戶線程而存在的,全部用戶線程結束以後,jvm就退出了,守護線程天然也結束,因此守護線程的結束時間不肯定數據庫

睡眠

線程的sleep方法(靜態方法),休眠當前線程,當前線程進入timed-waiting狀態api

yield

線程對象的yield方法代表該線程已經完成了最重要的工做,嘗試讓出cpu佔用,把cpu使用權讓給同優先級的其餘線程安全

中斷

interrupt()

經過調用線程對象的interrput方法中斷該線程,若是該線程處於等待/阻塞狀態,會拋出java.lang.InterruptedException異常。
該方法不能中斷處於I/O阻塞或synchronized阻塞的線程
線程對象的該方法被調用後,會設置阻斷標誌爲true,經過interrupted()方法檢查是否有人調用了本線程的interrupt方法多線程

interrupted()

檢查是否有人調用了本線程的interrupt方法併發

中斷的應用

線程池的shutdownNow方法就是經過調用全部工做線程的interrupt方法來中斷工做線程jvm

線程池

https://www.cnblogs.com/darknessplus/p/10359256.htmlide

線程安全的實現方法

不可變

互斥同步

synchronized關鍵字與ReentrantLock

都是可重入鎖
synchronized關鍵字由jvm實現,ReentrantLock由jdk實現
ReentrantLock提供比synchronized更高級的功能性能

  • 等待可中斷
  • 公平鎖
  • 選擇性通知

非阻塞同步

無同步

線程本地存儲

ThreadLocal類
ThreadLocal 不是用來協調多線程的,而是爲同一個線程的不一樣方法提供共享變量
一個線程的ThreadLocal變量在任何方法內都是可見的
線程Thread保存了ThreadLocalMap,ThreadLocalMap保存了本線程的全部ThreadLocal數據
ThreadLocal提供的方法

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }

線程之間的協調

多線程協做解決問題時,須要對他們的執行順序進行協調

join

調用另外一個線程對象的join方法表示本線程等待另外一個線程執行完成後再繼續執行

wait、notify、notifyAll

wait、notify是Object類提供的final方法,不可重寫。
經過調用某個對象的wait方法,釋放本線程對該對象持有的鎖,進入阻塞狀態。
另外一個線程調用該對象的notify方法,喚醒該對象的鎖的阻塞隊列中的一個線程,notifyAll方法喚醒該對象的等待隊列的全部線程。
wait期間,線程會釋放鎖,否則會死鎖。
配合synchronized使用,再同步代碼中使用。

wait與sleep的區別:

  • wait是Object方法,sleep是Thread的靜態方法
  • wait會釋放鎖,sleep不會

生產者消費者問題:

public class Wait {

    public static void main(String[] args) throws InterruptedException {
        LinkedList<Integer> list = new LinkedList<Integer>();
        Factory factory = new Factory();
        factory.setList(list);
        factory.setMaxSize(10);
        for(int i=1;i<=10;i++)
        {
            Producer producer = new Producer();
            producer.setNum(i);
            producer.setFactory(factory);
            producer.start();

            Consumer consumer = new Consumer();
            consumer.setNum(i);
            consumer.setFactory(factory);
            consumer.start();
        }
    }
}
class Producer extends Thread{
    private AbstractFactory factory;
    private int num;

    public void setNum(int num) {
        this.num = num;
    }

    public void setFactory(AbstractFactory factory) {
        this.factory = factory;
    }

    @Override
    public void run() {
        factory.produce(num);
    }
}
class Consumer extends Thread{
    private AbstractFactory factory;
    private int num;

    public void setFactory(AbstractFactory factory) {
        this.factory = factory;
    }

    public void setNum(int num) {
        this.num = num;
    }

    @Override
    public void run() {
       factory.consume(num);
    }
}
class Factory implements AbstractFactory{
    private List<Integer> list;
    private int maxSize;

    public void setList(List<Integer> list) {
        this.list = list;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    public void produce(int num) {
        synchronized (list)
        {
            while(num+list.size()>maxSize)
            {
                try {
                    System.out.println("倉庫已滿。要生產的"+num+",庫存容量"+maxSize);
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int size=list.size();
            for(int i=size;i<size+num;i++)
            {
                list.add(i);
            }
            System.out.println("生產"+num+"件,總共"+list.size()+"件");
            list.notifyAll();
        }
    }

    public void consume(int num) {
        synchronized (list)
        {
            while(list.size()-num<0)
            {
                try {
                    System.out.println("倉庫已空。要消費的"+num+",庫存容量"+list.size());
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int size=list.size();
            for(int i=size-1;i>=size-num;i--)
            {
                list.remove(i);
            }
            System.out.println("消耗"+num+"件,總共"+list.size()+"件");
            list.notifyAll();
        }
    }
}
interface AbstractFactory
{
    void produce(int num);
    void consume(int num);
}

await、signal、signalAll

對應着Object的wait和notify方法,Lock也有相似的機制。
經過Lock.newCondition()方法獲取一個condition對象,經過condition對象的await和signal方法進行線程同步。
在使用condition對象的await和signal方法以前必須得到重入鎖,調用signal方法後最好釋放重入鎖。

JUC包

AQS

CountdownLatch

控制一個線程等待其餘線程

public static void main(String[] args) throws InterruptedException {
        final CountDownLatch count = new CountDownLatch(10);
        for(int i=0;i<10;i++){
            new Thread(){
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    count.countDown();
                }
            }.start();
        }
        count.await();
        System.out.println("END");
    }

輸出:

Thread-0
Thread-2
Thread-3
Thread-1
Thread-5
Thread-4
Thread-6
Thread-7
Thread-8
Thread-9
END

Semaphore

信號量,控制併發線程數

public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(1);
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "正在運行");
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        System.out.println(Thread.currentThread().getName() + "結束");
                        semaphore.release();
                    }
                }
            }.start();
        }
    }

輸出:

Thread-5正在運行
Thread-5結束
Thread-0正在運行
Thread-0結束
Thread-3正在運行
Thread-3結束
Thread-8正在運行
Thread-8結束
Thread-6正在運行
Thread-6結束
Thread-2正在運行
Thread-2結束
Thread-7正在運行
Thread-7結束
Thread-1正在運行
Thread-1結束
Thread-4正在運行
Thread-4結束
Thread-9正在運行
Thread-9結束

其餘組件

FutureTask

得到任務的返回值

BlockingQueue

阻塞隊列,在線程池裏有應用

樂觀鎖和悲觀鎖

悲觀鎖

對數據更新的衝突持保守態度,認爲總會發生衝突。策略是在處理數據時加鎖

樂觀鎖

樂觀鎖認爲發生衝突的狀況比較少,不加鎖,而是在更新數據的時候檢查是否發生了衝突。

樂觀鎖的兩種實現

版本號機制

爲數據庫表增長版本號字段,每次更新數據版本號+1。在修改數據前獲取版本號,提交修改時檢查兩次版本號是否一致,如不一致說明數據更新發生了衝突。

CAS

Compare And Swap
現值V、舊值A、新值B,當且僅當V==A,更新B到數據庫
硬件實現的原子操做

樂觀鎖的缺點

ABA問題

CAS,V==A並不能說明V沒有發生過改變

自旋鎖開銷大

CAS與synchronized使用場景

CAS適用讀多寫少,synchronized適用寫多(衝突多)

synchronized和ReenTrantLock的區別

  • 都是可重入
  • synchronized由jvm實現,ReenTrantLock時jdk api
  • ReenTrantLock高級功能:中斷等待、公平鎖、選擇性通知
  • 二者性能相差無幾
相關文章
相關標籤/搜索