基礎Java多線程

創建線程的三種方式 

1:繼承thread類,重寫run方法

2:實現runnable接口

3:實現callable接口

第一種方式:繼承thread類

package com.wangjunji.qifeng;
//繼承thread類
public class TestCreateThread extends Thread {

    public TestCreateThread(String name) {
        super(name);
    }
    //覆蓋run方法
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            //如果繼承thread的類s可以使用this.getId 或 this.getName
            System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //創建對象
        TestCreateThread myThreadName = new TestCreateThread("myThreadName");
        TestCreateThread myThreadName1 = new TestCreateThread("myThreadName1");
        //調用run方法
        myThreadName.setName("我的子線程1");
        myThreadName1.setName("我的子線程2");
        myThreadName.start();
        myThreadName1.start();
        myThreadName.join();
        myThreadName1.join();
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
        }
    }
}

獲取和修改線程的名稱

獲取線程ID和線程名稱

1、在thread的子類中調用this.getid() 或this.getName() 

2、使用thread.currentThread().getId()和thread.currentThread.getName()

修改線程名稱

1、調用線程對象的setName方法

2、使用線程子類的構造方法賦值 看如上方法

案例:使用繼承thread類實現4個窗口賣100張票

package com.wangjunji.qifeng.second;

public class TiceWIn  extends Thread{
    private int count =100;

    @Override
    public void run() {
        int num = count;
        for (int i = 0; i < num; i++) {
            count = count -1;
            System.out.println("Thread name:"+this.getName()+":Thread id is :"+this.getId()+":"+count);
        }
    }
}



package com.wangjunji.qifeng.second;

import com.wangjunji.qifeng.Ticks;

public class Sellor {
    public static void main(String[] args) {
        TiceWIn t1 = new TiceWIn();
        TiceWIn t2 = new TiceWIn();
        TiceWIn t3 = new TiceWIn();
        TiceWIn t4 = new TiceWIn();
        t1.setName("一號窗口");
        t2.setName("二號窗口");
        t3.setName("三號窗口");
        t4.setName("四號窗口");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

四個人賣1000張票

package com.wangjunji.qifeng;

public class SellTickDoor  {
    public static void main(String[] args) throws InterruptedException {
        Ticks ticks = new Ticks();
        SellTicks t1 = new SellTicks(ticks);
        SellTicks t2 = new SellTicks(ticks);
        SellTicks t3 = new SellTicks(ticks);
        SellTicks t4 = new SellTicks(ticks);
        t1.setName("第一名");
        t2.setName("第二名");
        t3.setName("第三名");
        t4.setName("第四名");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t1.join();
        t2.join();
        t3.join();
        t4.join();

    }
}


package com.wangjunji.qifeng;

public class SellTicks extends Thread {
    private Ticks ticks;
    Object obj = new Object();
    public SellTicks(Ticks ticks) {
        this.ticks = ticks;
    }

    @Override
    public void run() {
        while (true) {
            if(ticks.getCount()>0) {
                synchronized (obj) {
                    ticks.sellcount();
                    System.out.println(this.getName() + "賣了一張" + "餘票還有" + ticks.getCount() + "張");
                }
                }else{
                break;
            }
        }
    }
}


package com.wangjunji.qifeng;

public class Ticks  {
    private int count =1000;

    public int getCount() {
        return count;
    }

    public void sellcount(){
        count = count -1 ;

    }
}

package com.wangjunji.qifeng;

public class MyRunable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
        }
    }
}



package com.wangjunji.qifeng;

public class Test {
    public static void main(String[] args) {
       /* MyRunable myRunable = new MyRunable();
        new Thread(myRunable).start();*/

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("名字" + Thread.currentThread().getName() + "Threadid" + Thread.currentThread().getId() + "i" + i);
                }
            }
        };
        new Thread(runnable).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
        }
    }

}

你和你女朋友共用一個銀行卡,你向卡中存錢,你女朋友從卡中取錢,使用程序模擬過程

package com.wangjunji.qifeng.card;

public class BankCard {
    private double money;

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}



package com.wangjunji.qifeng.card;

public class AddMony implements Runnable{
    BankCard bankCard;
    public AddMony(BankCard bankCard) {
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            double money = bankCard.getMoney();
            money = money+1000;
            bankCard.setMoney(money);
            System.out.println(Thread.currentThread().getName()+"存入一千元"+bankCard.getMoney());
        }
    }
}


package com.wangjunji.qifeng.card;

public class SubMony implements Runnable{
    BankCard bankCard;
    public SubMony(BankCard bankCard) {
        this.bankCard = bankCard;
    }

    @Override
    public void run() {
        for (int i = 0; i < 30; i++) {
            double money = bankCard.getMoney();
            if(money<0){
                System.out.println("餘額不足");
            }else{
                money = money-1000;
                bankCard.setMoney(money);
                System.out.println(Thread.currentThread().getName()+"取出一千元"+bankCard.getMoney());
            }

        }
    }
}
package com.wangjunji.qifeng.card;

public class TestBankCard {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard();
        AddMony ad = new AddMony(bankCard);
        SubMony  sd = new SubMony(bankCard);
        Thread chen = new Thread(ad,"晨晨");
        Thread bing = new Thread(sd,"冰冰");
        chen.start();
        bing.start();
    }
}

線程的基本狀態

線程對象被創建,即爲初始狀態  new 初始狀態 調用start後進入ready就緒狀態 os選中/時間片到期  running運行狀態

調用start()之後,進入就緒狀態,等待os選中,並分配時間片。

常見方法

休眠 public static void sleep 

設置線程優先級

優先級:

     線程對象.setPriority()

     線程優先級1-10,默認爲5 ,優先級越高,表示獲取cpu機會越多

守護線程:

     線程對象.setDaemon(true);設置守護線程

   線程有兩類:用戶線程(前強線程),守護線程(後臺線程)

如果程序中所有前臺線程都執行完畢,後臺線程會自動結束

垃圾回收器線程屬於守護線程 

package com.wangjunji.qifeng.card;

public class PriorityThread extends Thread {
    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }

    public static void main(String[] args) {
        PriorityThread priorityThread = new PriorityThread();
        PriorityThread priorityThread1 = new PriorityThread();
        priorityThread.setName("thread-1");
        priorityThread1.setName("thread-2");
        priorityThread.setPriority(10);
        priorityThread1.setPriority(1);
        priorityThread.start();
        priorityThread1.start();
    }
}

  線程安全問題:

A線程---查找下標0,時間片到期  --->將hello存入下標爲0的位置

B線程---查找下標0,時間片到期--->將world存入下標的位置

需求:A線程將hello存入數組

         B線程將world存入數組

多線程安全問題:

     當多線程併發訪問臨界資源時,如果破壞原子操作,可能會造成數據不一致。

    臨界資源:共享資源(同一對象),一次僅允許一個線絡使用,纔可能保證其正確性。

    原子操作,不可分割的多步操作,被視作一個整體,其順序和步驟不可打亂或缺省。

package com.wangjunji.qifeng;

public class ThreadSafe {
    private static int index=0;
    public static void main(String[] args) throws InterruptedException {

        String[] str = new String[5];
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                str[index] = "hello";
                index++;
            }
        };

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                str[index] = "world";
                index++;
            }
        };
        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable1);
        Thread thread3= new Thread(runnable);
        Thread thread4 = new Thread(runnable1);
        thread.start();
        thread1.start();
        thread3.start();
        thread4.start();
        thread.join();
        thread1.join();
        thread3.join();
        thread4.join();

        for (String s : str) {
            System.out.println(s);
        }
    }
}

思考:在程序中應用中,如何保證線程的安全性。

同步方式(1):

     同步代碼塊:synchronized(臨界資源對象) //對臨界資源對象加鎖

//代碼(原子操作)

package com.wangjunji.qifeng;

public class ThreadSafe {
    private static int index=0;
    public static void main(String[] args) throws InterruptedException {
      
        String[] str = new String[5];
        Runnable runnable1 = new Runnable() {
            @Override
            public void run() {
                synchronized (str){
                str[index] = "hello";
                index++;}
            }
        };

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                synchronized (str){
                    str[index] = "world";
                    index++;}
            }
        };
        Thread thread = new Thread(runnable);
        Thread thread1 = new Thread(runnable1);
        Thread thread3= new Thread(runnable);
        Thread thread4 = new Thread(runnable1);
        thread.start();
        thread1.start();
        thread3.start();
        thread4.start();
        thread.join();
        thread1.join();
        thread3.join();
        thread4.join();

        for (String s : str) {
            System.out.println(s);
        }
    }
}

每個對象都有一個互斥鎖標記,用來分配給線程

只有擁有對象互斥鎖標記的線程,才能進入對該對象加鎖的同步代碼塊。

   同步方法:

synchronized返回值類 方法名稱(形參列表0) //對當前對象this加鎖 ,如果是靜態方法則是 class

同步方法:

注:只有擁有對象的互斥標記的線程,才能進入該對象加鎖的同步方法中。

線程退出同步方法時,會釋放相應的互斥鎖標記。

 

同步規則:

注意:只有在調用包含同步代碼塊的方法,或者同步方法時,才需要對象的鎖標記。

如調用不包含不包含代碼的方法,或普通方法時,則不需要鎖標記,可直接調用。

已知jdk中線程安全的類

stringbuffer

vector

hashtable

以上類中的公開方法,均爲synchnoized的同步方法。

 

經典問題:

死鎖:

當第一個線程擁有a對象鎖標記,並等待b標記,同時第二個線程擁有b對象鎖標記,並等待A對象鎖標記時,產生死鎖。

一個線程可以同時擁有多個對象的鎖標記,當線程阻塞時,不會釋放已經擁有的鎖標記,因此可能造成死鎖。

 

//死鎖代碼
package com.wangjunji.qifeng.card;

public class Resrouce  {
    public static Object objA = new Object();
    public static Object objB = new Object();
}

//
package com.wangjunji.qifeng.card;

public class BoyInfo extends Thread {
    public void run(){
        synchronized (Resrouce.objA){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("boygetInfo get a");
            synchronized (Resrouce.objB){
                System.out.println("boygetinfo get b ");

            }
        }
    }
}

//
package com.wangjunji.qifeng.card;

public class grirlInfo extends Thread{
    public void run(){
        synchronized (Resrouce.objB){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("grirlInfo get b");
            synchronized (Resrouce.objA){
                System.out.println("grirlInfo get a ");

            }
        }
    }
}

package com.wangjunji.qifeng.card;

public class UseInfo {
    public static void main(String[] args) {
        BoyInfo boyInfo = new BoyInfo();
        grirlInfo grilinfo = new grirlInfo();
        boyInfo.start();
        grilinfo.start();
    }
}

線程通信

等待:等待隊列

public final void wait()

public final void wait(long timeout )

必須在對obj加鎖的同步代碼塊中,在一個線程中,調用obj.wait()時,此線程會釋放其擁有所有鎖標記,同時此線程阻塞在o的等待隊列中,釋放鎖,進入等待隊列。

通知:

public final void notify()

public final void notifyall()

經典問題:

生產者,消費者

若干個生產者在生產產品,這些產品將提供給若干個消費者去消費,爲了使用生產者和消費者能併發執行,在兩者之間一個能存儲多個產品的緩衝區,生產者將生產的產品放入緩衝區中,消費者從緩衝區中取走產品進行消費,顯然生產者和消費者之間必須保持同步,即不允許消費者到一個空的緩衝區中取產品,也不允許生產向一個滿的緩衝區中放入產品。

 

線程的創建:

繼承thread 

實現runnable接品,傳入給thread對象執行。

 

線程安全:

      同步方法塊:爲方法中的局部代碼加鎖

     同步方法,爲方法的所有代碼加鎖

高級多線程:

 1)線程池概念

問題:

    線程是寶貴的內存資源,單個線程約佔1Mb空間,過多分配易造成內存溢出。

   頻繁的創建及銷燬線程會增加虛擬機回收頻率,資源開銷,造成程序性能下降。

池程池:

   線程容器,可設定線程分配的數量的上限。

    將預先創建的線程對象存入池中,並重用線程池中的線程對象

    避免頻繁的創建和銷燬。

線程池原理:

  task1

   task2        -----> threadpool  

   task3

將任務提交給線程池,由線程池分配線程,運行任務,並在當前任務結束後複用線程。

創建線程池:

常用的線程池接口和類(所有包java.util.concurrent裏)

Executor  線程池的頂級接口

executorservice:線程線接口,可通過submit(runable task)提交任務代碼。

executors工廠類,通過此類可以獲得一個線程池。

通過newFixedThreadpool(int nthreads)獲取固定數量的線程池,參數指線程池中線程的數量。

通過newCachedthreadpools獲得動態數量的線程池,如不夠則創建新的,沒有上限。

創建線程池

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 演示線程池的創建
 * Executor:線程池的根接口,execute()
 * executorService:包含管理線程池的一些方法,submit shutdown
 *  threadpoolExecutor
 *  scheduledthreadpoolexecutor
 * executor:創建線程池的工具類
 *   (1) 創建固定線程個數池程池
 *   (2) 創建緩存線程池,由任務的多少決定
 *     (3):創建單線程池
 *     (4)創建調度線路程池,調度,週期,定時執行。
 *
 */
public class Demo01 {
    public static void main(String[] args) {
        //設置提交的個數
        //ExecutorService executorService = Executors.newFixedThreadPool(4);
        //不設置提交的個數,創緩存線程池,線程個數
        ExecutorService executorService = Executors.newCachedThreadPool();
        // 創建單線程Executors.newSingleThreadExecutor();
        //創建調度線程池,調度,週期,定時執行 Executors.newScheduledThreadPool(5);
        Runnable runnable = new Runnable() {
            private int ticket = 10000;
            public void run() {
                while (true){
                    if(ticket<=0){
                        break;
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName()+"買了第"+ticket+"張票");
                }
            }
        };
        //提交任務
        for (int i = 0; i < 4; i++) {
            executorService.submit(runnable);
        }
        //關閉線程,不然程序結束不了,等待所有任務執行完畢,才能結束。
         executorService.shutdown();

    }
}

Callable接口:

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception; 
}

jdk1.5加入,與runnable接口類似,實現之後代表一個線程任務

callable具有泛型返回值,可以聲明異常。

package com.wangjunji.qifeng.threadpool;


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

/**
 * 演示callable接口的使用
 * callable和runable接口的區別
 * callable接口中call方法有返回值,runbable中run方法沒有返回值
 * callable接口中call方法有聲明異常,runable接口中run方法沒有異常
 */
public class Demo02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //功能需求,使用callable實現1~100的和
        //創建callable對象
        Callable<Integer> callable= new Callable<Integer>() {
            @Override

            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName()+"開始計算");
                int sum=0;
                for (int i = 1; i <= 100; i++) {
                    sum=sum+i;
                }
                return sum;
            }
        };
        //2。不能交給線程。需要把callable對象轉成任務
        FutureTask<Integer> task = new FutureTask<>(callable);
        //創建線程
        Thread thread = new Thread(task);
        //啓動線程
        thread.start();
        //獲取 結果,等等call執行完畢,返回結果
        Integer integer = task.get();
        System.out.println(integer);
    }
}

使用callable與線程池結合起來使用

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.*;

/**
 * callable與線程池結合起來用
 * 使用線程池來計算1-100的和
 */
public class Demo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //提交任務,表示將要執行完任務的結果
        Future<Integer> taskinfo = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("開始執行作務");
                int sum = 0;
                for (int i = 0; i <= 100; i++) {
                    sum = sum + i;
                }
                return sum;
            }
        });
        //3獲取結果
        System.out.println(taskinfo.get());
        executorService.shutdown();

    }
}

Future接口 :

future表示將要完成任務的結果

需求,使用兩個線程,併發計算1~50,51~100的和,再進行彙總計算。

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.*;

public class Demo04 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService task =   Executors.newFixedThreadPool(2);
        Future<Integer> item1 = task.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 0; i < 50; i++) {
                    sum = sum + i;
                }
                return sum;
            }
        });
        Future<Integer> item2 = task.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 50; i <= 100; i++) {
                    sum = sum + i;
                }
                return sum;
            }
        });
        int allcount =item1.get()+item2.get();
        System.out.println(allcount);
        task.shutdown();

    }
}

future接口

表示executorservice.sumbit所返回的狀態結果,就是call的返回值

方法:v get()以阻塞形式等待,future中的異步處理結果(call的返回值)

思考什麼是同步,什麼是異步

同步:形容一次方法調用,同步一旦開始,調用者必須等待該方法返回,才能繼續

異步:形容一次調用,異步一旦開始,像是一次消息傳遞,調用者告知之後立刻返回,二者競爭時間片,併發執行。

Lock接口:

jdk5加入,與synchronized比較,顯示定義,結構更靈活。

提供更多實用方法,功能更強大,性能更優越。

常用方法:

void lock()://獲取鎖,如鎖被佔用,則等待

boolean tryLock()://嘗試獲取鎖,成功返回true,失敗返回false,不阻塞

void unlock釋放鎖。

重入鎖:

拿多次鎖,稱爲重入鎖

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo06 implements Runnable {
    private int tickCount = 100;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            lock.lock();
            try{
                if(tickCount<=0){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+":"+tickCount);
                tickCount--;
            }finally {
                lock.unlock();
            }
        }



    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        Demo06 demo06 = new Demo06();
        executorService.submit(demo06);
        executorService.submit(demo06);
        executorService.submit(demo06);
        executorService.submit(demo06);
        executorService.shutdown();
    }
}

讀寫鎖:

reentantreadwritelock

一種支持一寫多讀的同步鎖,讀寫分離,可分別分配讀鎖,寫鎖

支持多次分配讀鎖,使用多個讀操作可以併發執行。

互斥規則,

寫一寫,互斥,阻塞

讀-寫,互斥,讀阻塞,寫阻塞讀

讀一讀,不互斥,不阻塞

在讀操作遠遠高於寫操作的環境中,可保障線程安全情況下,提高運行效率。

package com.wangjunji.qifeng.threadpool;


import java.security.Security;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

public class Demo07 {
    private ReentrantReadWriteLock rr1 = new ReentrantReadWriteLock();

    private ReadLock readLock = rr1.readLock();
    private WriteLock writeLock = rr1.writeLock();
    private String value;

    public String getValue(){
        readLock.lock();
        try{
            try {
                Thread.sleep(1000);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("讀取value"+value);
            return this.value;
        }  finally {
            readLock.unlock();
        }


    }

    public void setValue(String value){
        writeLock.lock();
        try{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("寫入value"+value);
            this.value = value;
        }finally {
            writeLock.unlock();
        }
        this.value = value;
    }


    public static void main(String[] args) {
        Demo07 demo07 = new Demo07();
        //創建20個的線程池
        ExecutorService executorService = Executors.newFixedThreadPool(40);
        for (int i = 0; i < 22; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    demo07.setValue(Thread.currentThread().getName()+new Random().nextInt(100));
                }
            });
        }

        for (int i = 0; i < 18; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    demo07.getValue();
                }
            });
        }

        executorService.shutdown();
        //如果是true,則執行完成
        executorService.isTerminated();
    }

}

線程安全的集合

collection體系集合中,除vector以外的線程安全集合。

package com.wangjunji.qifeng.threadpool;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

public class Dem09 {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<>();
//使用collections中的線程安全方法轉換線程安全的集合。
        Collection<String> strings = Collections.synchronizedCollection(arrayList);
//java併發包裏面的
 /*ArrayList<String> arrayList = new ArrayList<>();
        Collection<String> strings = Collections.synchronizedCollection(arrayList);*/
        CopyOnWriteArrayList<String> strings = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            int temp = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i1 = 0; i1 < 10; i1++) {

                        strings.add(Thread.currentThread().getName()+":"+temp+":"+i1);
                        System.out.println(strings.toString());
                    }
                }
            }).start();
        }
    }
}

CopyOnWriteArrayList:

線程安全的arraylist,加強版的讀寫分離

寫有鎖,讀無鎖,讀寫之間不阻塞,優於讀寫鎖。

寫入時,先copy一個容器副本,再添加新元素,最後替換引用。 以空間來換取安全。

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo10 {
    public static void main(String[] args) {
        CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for (int i1 = 0; i1 < 100; i1++) {
                        list.add(i1);
                    }
                }
            });
        }
  
        //這個結束的時候,有可能所有的子任務還沒有完成,需要增加以下的代碼
        executorService.shutdown();
        System.out.println(list.size());
        while (!executorService.isTerminated()){

        }
        System.out.println(list.size());
    }
}

CopyOnWriteArraySet

線程安全的Set,底層使用copyOnWriteArrayList實現

唯一不同在於,使用addifAbsent添加元素,會遍歷數組

如果存在元素,則不添加(扔掉副本)

Quene接口(隊列)

collection的子接口,表示隊列Fifo()先進先出 

棧,先進後出

常用方法:

    都需要拋出異常

   boolean add(E e):順序添加一個元素(到達上限後,再添加則會拋出異常)

  E remove 獲得一個元素並移除(如果隊列沒有元素,則拋出異常)

  E element() 獲得一個元素,但不移除,如果隊列沒有元素,則拋出異常。

返回特殊值:推薦使用

boolean offer(E e)//順序添加一個元素(到達上限後,再添加則會返回false)

E poll獲得一個元素並移除 (如果隊列沒有元素時,則返回null)

E peek() 獲得一個元素但不移除 (如果隊列沒有元素時,則返回null)

package com.wangjunji.qifeng.threadpool;

import java.util.LinkedList;
import java.util.Queue;

public class Demo11 {
    public static void main(String[] args) {
        Queue<String> fruits = new LinkedList<>();
        fruits.offer("蘋果");
        fruits.offer("葡萄");
        fruits.offer("香蕉");
        fruits.offer("梨子");
        fruits.offer("葡萄");
        fruits.offer("蘋果");
        fruits.offer("葡萄");
        fruits.offer("香蕉");
        fruits.offer("梨子");
        fruits.offer("葡萄");
        System.out.println(fruits.size());
        int length = fruits.size();
        for (int i = 0; i < length; i++) {

            System.out.println(fruits.poll());
        }
    }
}

ConcurrentLinkedQueue

線程安全、可高效讀寫的隊列,高併發下性能最好的隊列

無鎖,cas 比較交換算法,修改的方法包含三個核心參數(Ven)

v要更新的變量

E預期值

N新值。

只有當v==e時,v=n,否則表示已被更新過,則取消當前操作。

BlockingQueue接口(阻塞隊列)

Queue的子接口,阻塞隊列,增加了兩個線程狀態爲無限期的等待的方法。

方法

void put(E e)將指定的元素插入此隊列中,如果沒有可用空間,則等待

E take 獲得並移除此隊列的頭部元素,如果沒有可用元素,則等待。 

可用於解決產生、消費的問題

阻塞隊列

ArrayBlockingQueue:

數組結構實現,有界隊列(手工固定上限)

BlockingQueue<String> abq = new ArrayBlockingQueue<String> (10);

LinkedBlockingQueue 

鏈表結構實現,有界隊列(默認上限integer.Max_Value)

BlockingQueue<String> abq = new LinkedBlockingQueue <String> (10);

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;

/**
 * 阻塞隊列的使用
 * 案例1:創建一個有界隊列,添加數據
 * 案例2:使用阻塞隊列實現生產者和消費者
 */
public class Demo13 {
    public static void main(String[] args) throws Exception {
        ArrayBlockingQueue<String> queue  = new ArrayBlockingQueue<>(3);

            queue.put("aaa");
            queue.put("aaa");
            queue.put("aaa");
            queue.put("aaa");
            

    }
}

生產者,消費者

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ArrayBlockingQueue;

public class Demo14 {
    public static void main(String[] args) {
        ArrayBlockingQueue<Integer> queue  = new ArrayBlockingQueue<>(6);
        Thread zhengzheng = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {

                    try {
                        System.out.println("生產了第" + i + "個麪包");
                        queue.put(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread bingbing = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {

                    try {
                        System.out.println("消費" + queue.take() + "個麪包" );

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

        zhengzheng.start();
        bingbing.start();

    }
}

ConcurrentHashMap:

初始容量默認爲16段,使用分段鎖設計

不對整個map加鎖,而是爲每個segment加鎖

當多個對象存入同一個segment時,才需要互斥

最理想的狀態爲16個對象分別存入16個segemnt,並行數量爲16.

使用方式與hashmap無異

package com.wangjunji.qifeng.threadpool;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo15 {
    public static void main(String[] args) {
        ConcurrentHashMap<String,String> hashMap = new ConcurrentHashMap<String,String>();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    for (int i1 = 0; i1 < 1000; i1++) {
                        hashMap.put(Thread.currentThread().getName()+":"+i1,"value"+ String.valueOf(Math.random()*1000));
                    }
                }
            });

        }
        executorService.shutdown();
        while (!executorService.isTerminated()){

        }

        System.out.println(hashMap);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章