java多線程與併發--double_happy

CAS

結合 高併發 文章看!!!! 要不然看不懂

compare and swap. :
	比較 和 交換    
	在多線程 沒有鎖的 情況下 可以保證 多線程對一個 共享變量的 更新


使用:
	拿到內存中的最新值,使用CAS嘗試將內存的值修改成目標值 如果修改失敗,
	則獲取內存位置的最新值,然後重新嘗試,直到修改成功

注意:
	當前值,內存中最新值,目標值

 當前值 與 內存中最新值 進行比較 相等 就更新 目標值    不相等 說明 其他線程修改了這個共享變量的值  則然後重新嘗試讀取 當前值 與內存中最新值進行比較,直到修改成功 



CAS  缺點:
	開銷大  一直do while
  它是可以保證原子性的
  
底層代碼
 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

開銷大 那麼怎麼辦呢?  

ABA的問題 :
	表示  原始共享變量是 0   
	1號線程 讀取的時候是 0     這個時候
	2號線程  讀取0  修改成 2 
	3號線程 讀取 2 修改成 0
	這個時候 1號線程 讀取的 0  實際上  是 變了樣的 0 

這就是 aba 問題  。  

就好比:
	你女朋友 分手之後 找了一個男的 之後 又跟你 複合了
所以 中間發生了什麼 你要感知到 才行 。


解決ABA問題:
	共享變量 再 添加一個 版本號  即可    
	就是 讀取共享變量的時候  不僅讀取值 也要 讀取版本號 

那麼 CAS 的底層實現是什麼呢???
	cas 和 sychroned 和 volatile 的底層實現 都一樣

JUC

我們使用 多線程的目的 :
	就是提高效率 
	就是 儘可能的 利用 cpu資源   儘可能的利用系統資源 

如果 多線程使用 不合理 那麼 可能導致性能 還不如 單線程 導致性能 更低


因爲 多線程消耗的資源更多  涉及到 線程之間的調度 cpu 上下文之間的 切換 
以及 線程的 創建及銷燬    還有同步  都是多線程的問題。	
jdk 1.5 之前 :
	sychornizde  和 volitail 
	就提供這兩個 用於多線程的

1.5之後  jdk 提供了  java.util.current 包 這裏面提供了 大量 線程相關的 工具類
那麼 多線程 就是 你 
	如何利用更多的資源 做更多的事情

volatile 關鍵字

1.可見性
2. 有序性

它是不保證 原子性的
package com.juc;

public class VolatileAPP {

    public static void main(String[] args) {

        /**
         *
         */

        Data data = new Data();

        new Thread(()->{

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            data.flag = true;

            System.out.println("flag = " +data.isFlag());

        }).start();

        
        while(true){
            if(data.isFlag()){
                System.out.println("-------------");
                break;
            }
        }



    }
}


class  Data {

    boolean flag = false;

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
你可以使用 Data implements  Runnable  來實現 線程裏面的 run方法 

我個人是不習慣  還是喜歡使用 lamda表達式的方式  因爲我 scala 用的比 java熟悉 

上面的代碼:
	兩個線程:
		Thread  用來 修改 flag值
		main 用來 反覆while ture 去讀取flag值

那麼 上面的 -----會被打印出來嗎??
以及 while true 能不能結束?

在這裏插入圖片描述

結果:
	說明  兩個線程之間 共享數據 是不可見的 

因爲 thread 線程 以及 把 flag 修改成 true 
而 main 線程 讀取的 flag  卻是 flag

說明是有問題的:
	說明 兩個線程之間 共享數據操作 是不可見的 
解決:
	1. synchronized 
	      這個關鍵字 你加在哪???看高併發的文章!!
	      	1.加在 isFlag方法那塊 
	      	2.或者 你加在 while true 裏面的if 都可以
	2. volatile     
class  Data {

    boolean flag = false;

    public synchronized boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}


我修改的是 這 
synchronized  效率極低 :
	  加鎖這種效率是最低的  
	  如果多個線程 來 訪問isFlag 方法 那麼 需要判斷 會發生阻塞! 效率很低


使用 volatile :
	他就是 多線程情況下操作 共享數據 是可以保證 共享數據的可見性

即:
class  Data {

    volatile boolean flag = false;

    public  boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}


但是 volatile 是不能保證 原子性的!

原子性:
	你可以理解爲  :
		一次操作 要麼成功 要麼失敗    所以你一晚上 幾次??

原子性

i++ 的原子性問題!:
	很好的一個案例

結合cpu cache : 你可以知道
	i++ 操作 實際上是 三步:
		1.從主存 讀取 i   放到 cpu cache裏
		2. 線程 讀取 cpu cache值 進行修改
		3.  修改後的 cpu cache值 寫回 主存
	讀 --》修改---》寫回

這三步 每一步都是具有原子性的  但是 
三步結合在一起 是不具有原子性的!!!
package com.juc;

public class AtomicApp {

    public static void main(String[] args)  {


        AtomicData atomicData = new AtomicData();

        for (int i = 0; i <20 ; i++) {

            new Thread(()->{

                for (int j = 0; j <1000 ; j++) {
                    atomicData.increate();
                }

            },"AtomicApp"+i).start();

        }

        while (Thread.activeCount() > 2) {  //>2 因爲一個 main 一個gc
            Thread.yield();
        }
        
        System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);


        //單線程 
//        for (int j = 0; j <1000 ; j++) {
//            atomicData.increate();
//        }
//
//        System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);




    }
}

class  AtomicData {

    int number = 0;

    public int increate(){
        return number++;
    }

}
結果:
	單線程下 結果是 1000  是沒有問題的 
	多線程情況下 正常結果應該是 20000 但是 不是!!

這就說明  number++; 操作 是不具有原子性  在多線程下 是用問題的!!!


但是真正的理解是:
	number ++  是三步操作  
		多線程下 你會覺得是 可見性導致的  但是 不準確  因爲 原子性!!
		如果是可見性 你加一個volatile 即可 但是 number++ 三步在一起 是不具有原子性的 
		volatile 是不能解決 原子性的 

要想解決:
	1。原子變量  
		jdk1.5之後提供的  java.util.concurrent.atomic包下 
	2.sychronized  
package com.juc;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicApp {

    public static void main(String[] args)  {


        AtomicData atomicData = new AtomicData();

        for (int i = 0; i <20 ; i++) {

            new Thread(()->{

                for (int j = 0; j <1000 ; j++) {
                    atomicData.increase2();
                }

            },"AtomicApp"+i).start();

        }

        while (Thread.activeCount() > 2) {  //>2 因爲一個 main 一個gc
            Thread.yield();
        }


        System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);
        System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.atomicInteger);


        //單線程
//        for (int j = 0; j <1000 ; j++) {
//            atomicData.increate();
//        }
//
//        System.out.println(Thread.currentThread().getName()+",number is : " +atomicData.number);




    }
}

class  AtomicData {

    int number = 0;

//第一種解決
    public synchronized int increate(){
        return number++;
    }

//第二種解決
    AtomicInteger atomicInteger = new AtomicInteger();
    public  void increase2(){
        atomicInteger.getAndIncrement();
    }

}
文章開始提到:
	CAS算法是可以解決 原子性問題

那麼原子類 就是調用 CAS算法!!  文章最開始就是源碼
原子變量:
	以AtomicInteger 爲例 看源碼

AtomicInteger源碼:

public class AtomicInteger extends Number implements java.io.Serializable {

  private volatile int value;
  
	public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
}

getAndIncrement 實現:

 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

compareAndSwapInt 實現的就是 cas算法

結論:原子變量
1. volatile int value;  保證值 的 可見性
2. CAS算法保證 數據的原子性


CAS:
	當前值,內存中最新值,目標值
	可以理解爲:
		內存值V
		內存中最新值A
		更新值B
		當且僅當 V==A ,把 B的值 賦值給 V   即 多線程情況下 只有一個線程會成功

cas效率高 是因爲 它不會阻塞  但是如果要自己實現cas的  需要對 爲修改成功後的步驟該
如何做 要自己實現  類似原子類裏面的這個方法!!

下圖 就是cas 過程

在這裏插入圖片描述
模擬CAS算法

package com.juc;

public class CASAPPComplement {

    public static void main(String[] args) {


        CompareAndSwap cas = new CompareAndSwap();

        for (int i = 0; i <10 ; i++) {

            new Thread(()->{

                int expectValue = cas.get();

                boolean sucess = cas.compareAndSet(expectValue, (int) (Math.random() * 101));

                System.out.println("是否成功:"+sucess);

            }).start();

        }

    }
}

class CompareAndSwap{

    int value;

    //獲取內存值
    public synchronized int get(){
        return value;
    }

    //比較
    public synchronized int compareAndSwap(int expectValue, int newValue) {

        int oldValue = value;

        if (oldValue == expectValue) {
            this.value = newValue;
        }

        return oldValue;
    }

    //設置值
    public synchronized boolean compareAndSet(int expectValue, int newValue){
        return expectValue == compareAndSwap(expectValue, newValue);
    }
}
底層  絕對不是用 synchronized 這個的哈

ConcurrentHashMap 鎖分段機制

是一個線程安全的 HashMap    

它是jdk1.5 之後那個包 裏面提供的

那麼HashMap 和HashTable 有什麼區別??

HashMap 是線程不安全的 
HashTable 是線程安全的     效率低!鎖表  實際上就是 並行轉換成 串行的一個操作!!

如果HashTable 遇到 複合操作 就是線程不安全的!:
	eg:
		若不存在就添加
if(!table.contants()){
 table.put
}

contants 和put 方法各自具有 獨立的鎖  但是 和在一起 就不具有獨立的鎖  有線程問題!!

ConcurrentHashMap:鎖分段機制

	concurrentLevel  : 分段級別 默認的級別是16
	也就是ConcurrentHashMap 裏面有16 段 每一段 叫 segment
	每個 segment 有獨立的表 
	好處就是 :
		每個 segment 都有一個獨立的鎖
   那麼 多線程情況下 就可以併發訪問不同的 segment  效率高

1.8 之後 每隔segment 採用的是 cas  1.5採用的是 鎖



注意:
  ConcurrentHashMap   -》HashMap
  ConcurrentSkipListMap -》TreeMap
  CopyOnWriteArrayList -》 ArrayList
  CopyOnWriteArraySet  -> ArrayAet

這些多線程容器都優於 jdk原生的	
那麼他們都解決什麼問題呢??

package com.juc;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * 寫入 並複製
 */
public class CopyOnWriteArrayListApp {
    public static void main(String[] args) {

        ListTest listTest = new ListTest();

        for (int i = 0; i <10 ; i++) {

            new Thread(()->{

                Iterator<String>  it = listTest.list.iterator();

                //邊取 邊加
                while (it.hasNext()) {
                    System.out.println(it.next());

                    listTest.list.add("AA");
                }

            }).start();

        }



    }
}

class  ListTest{

    static  List<String> list =  Collections.synchronizedList(new ArrayList<String>());

   static {
       list.add("AA");
       list.add("BB");
       list.add("CC");
   }


}
結果:
	發生  併發修改 異常
java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.juc.CopyOnWriteArrayListApp.lambda$main$0(CopyOnWriteArrayListApp.java:24)
	at java.lang.Thread.run(Thread.java:748)


因爲 讀取 和添加 操作的 是 同一個數據源 就會發生 併發修改異常! 你一個線程跑 也是這個問題 

也就是:
	不能  讀取 和添加 操作的 是 同一個數據源

使用 CopyOnWriteArrayList  可以解決
class  ListTest{

//    static  List<String> list =  Collections.synchronizedList(new ArrayList<String>());
    
    static  CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

   static {
       list.add("AA");
       list.add("BB");
       list.add("CC");
   }
}


注意:
但是 添加操作 不適合它 因爲是寫入並複製  複製!!

併發的迭代 操作 它的效率很高!!

CountDownLatch 閉鎖

也就是 
	當你要完成一些運算 這些運算沒有完成 那就等待這些運算完成
	我在繼續執行!!
package com.juc;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchApp {
    public static void main(String[] args) {


        CountDownLatch latch = new CountDownLatch(10);

        long start = System.currentTimeMillis();

        //打印 5w 以內偶數
        for (int i = 0; i <10 ; i++) {

            new Thread(()->{

                for (int j = 0; j <50000 ; j++) {

                    if (j % 2 == 0) {
                        System.out.println(j);
                    }
                }
            }).start();

        }


        long end = System.currentTimeMillis();

        System.out.println("執行時間: " +(end -start));
        

    }
}


上面代碼 想 計算:
	10個線程 做 5w以內的偶數打印 的執行時間 

是沒有辦法計算的 :
	因爲:
		main 和 10個線程 是同時執行的   上面的代碼 算得不準確   

  不能保證 10個線程 執行完了  再計算 執行時間 !!!!

所以有個執行的先後順序的 !!!


解決這個問題:如下
	必須用到 閉鎖!!
package com.juc;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchApp {
    public static void main(String[] args) throws InterruptedException {


        CountDownLatch latch = new CountDownLatch(10);

        long start = System.currentTimeMillis();

        //打印 5w 以內偶數
        for (int i = 0; i <10 ; i++) {

            new Thread(()->{

                for (int j = 0; j <50000 ; j++) {

                    if (j % 2 == 0) {
                        System.out.println(j);
                    }
                }

                latch.countDown();
            }).start();

        }

        latch.await();

        long end = System.currentTimeMillis();

        System.out.println("執行時間: " +(end -start) +"ms");


    }
}

結果:

在這裏插入圖片描述

CountDownLatch latch = new CountDownLatch(10);



10 : 完成一個線程操作     就遞減1    直到減到 0

main 線程 才執行 


所以:
	1. 10個線程裏 要有  latch.countDown();  
	2. main   有一個 latch.await()


所以 閉鎖這個操作 應用很廣泛  很重要!!

Lock同步鎖

用於 解決多線程安全的方式:
	1. 同步代碼塊      synchronized   可以稱爲隱式鎖
	2. 同步方法       synchronized    可以稱爲隱式鎖
	
	jdk1.5 之後  更靈活的方式!!!
	3. 同步鎖
	   是一個顯示鎖 需要 通過lock() 方法上鎖,
	   必須通過unlock() 方法進行釋放鎖

注意:
	必須通過unlock() 方法進行釋放鎖
如果不釋放會有很多問題 最好放在 finally裏面


而 synchronized 就不需要 它是 jvm 底層幫自己維護
package com.juc;

import java.util.concurrent.atomic.AtomicInteger;

public class LockApp {

    public static void main(String[] args) {

        Ticket ticket = new Ticket();

        for (int i = 0; i < 3; i++) {

            new Thread(()->{

                while (true) {

                    if (ticket.getTicket() > 0) {
                        System.out.println(Thread.currentThread().getName()+" 完成售票,餘票爲:"+ --ticket.ticket);
//                        System.out.println(Thread.currentThread().getName()+" 完成售票,餘票爲:"+ ticket.a.decrementAndGet());
                    }

                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },i+"號窗口").start();
        }


    }
}

class Ticket{

    int ticket = 100;

//    AtomicInteger a = new AtomicInteger(100);


    public int getTicket() {
        return ticket;
    }

}
上面的一個經典案例:
	賣票!!

你可以使用 原子類來解決 

那麼使用 Lock 該如何做呢??

package com.juc;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockApp {

    public static void main(String[] args) {

        Ticket ticket = new Ticket();

        Lock lock = new ReentrantLock();

        for (int i = 0; i < 3; i++) {

            new Thread(() -> {

                //上鎖
                lock.lock();

                try {
                    while (true) {

                        if (ticket.getTicket() > 0) {
                            System.out.println(Thread.currentThread().getName() + " 完成售票,餘票爲:" + --ticket.ticket);
                        }

                        try {
                            Thread.sleep(200);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                } finally {
                  //釋放鎖
                    lock.unlock();
                }


            }, i + "號窗口").start();
        }


    }
}

class Ticket {

    int ticket = 100;

    public int getTicket() {
        return ticket;
    }

}

等待喚醒機制

synchronized:
	它是 wait 和 notify 來完成 但是效率低 對吧!!

那麼 如何使用 lock 同步鎖 來實現   等待喚醒機制!!!呢??

package com.juc;

public class ProducerAndConsumerApp {
    public static void main(String[] args) {

        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);
        
        
        
        new Thread(()->{

            //生產貨物
            for (int i = 0; i <20 ; i++) {
                producer.clerk.get();
            }
            
        },"producer").start();
        
        new Thread(()->{
            
            //賣貨
            for (int i = 0; i <20 ; i++) {
                consumer.clerk.sale();
            }
            
        },"consumer").start();
        
        

    }
}


//店員
class Clerk{
    
    int product = 0;
    
    //進貨
    public synchronized void  get(){
        if (product >= 10) {
            System.out.println("產品已滿!");
        }else {
            System.out.println(Thread.currentThread().getName()+" : " + ++product);
        }
    }
    
    //賣貨
    public  synchronized void sale(){
        if (product <= 0) {
            System.out.println("缺貨!");
        }else {
            System.out.println(Thread.currentThread().getName()+" : " + --product);
        }
    }
}

//producer
class Producer{
    
    Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }
    
}

class Consumer{

    Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

}

結果:
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
consumer : 7
consumer : 6
consumer : 5
consumer : 4
consumer : 3
consumer : 2
consumer : 1
consumer : 0
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
缺貨!
producer : 1
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
producer : 9
producer : 10
產品已滿!
產品已滿!
上面是 生產者和消費者案例 :
	1.沒有采用 等待喚醒機制 會有什麼問題

生產者 不斷髮送數據  消費值不斷消費 !!
會造成 數據丟失問題!! 或者數據重複消費 問題!!
就看 誰生產快 和消費快了  這是有問題的!!

加入等待喚醒機制!
package com.juc;

public class ProducerAndConsumerApp {
    public static void main(String[] args) {

        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);



        new Thread(()->{

            //生產貨物
            for (int i = 0; i <20 ; i++) {
                producer.clerk.get();
            }

        },"producer").start();

        new Thread(()->{

            //賣貨
            for (int i = 0; i <20 ; i++) {
                consumer.clerk.sale();
            }

        },"consumer").start();



    }
}


//店員
class Clerk{

    int product = 0;

    //進貨
    public synchronized void  get(){
        if (product >= 10) {
            System.out.println("產品已滿!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else {
            System.out.println(Thread.currentThread().getName()+" : " + ++product);

            this.notifyAll();
        }
    }

    //賣貨
    public  synchronized void sale(){
        if (product <= 0) {
            System.out.println("缺貨!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else {
            System.out.println(Thread.currentThread().getName()+" : " + --product);

            this.notifyAll();
        }
    }
}

//producer
class Producer{

    Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

}

class Consumer{

    Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

}
結果:
producer : 1
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
producer : 9
producer : 10
產品已滿!
consumer : 9
consumer : 8
consumer : 7
consumer : 6
consumer : 5
consumer : 4
consumer : 3
consumer : 2
consumer : 1
consumer : 0
缺貨!
producer : 1
producer : 2
producer : 3
producer : 4
producer : 5
producer : 6
producer : 7
producer : 8
producer : 9
consumer : 8
consumer : 7
consumer : 6
consumer : 5
consumer : 4
consumer : 3
consumer : 2
consumer : 1
consumer : 0


只要發現:
	生產滿了 就趕緊消費
	缺貨 就趕緊生產

這樣纔是 有效的數據!!  
這種是效率低的問題嗎???

改一下 :
	把生產者 生產貨物 變成1
消費完 再 睡200ms

會造成 程序一隻運行 不會結束!!
package com.juc;

public class ProducerAndConsumerApp {
    public static void main(String[] args) {

        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);



        new Thread(()->{


            //生產貨物
            for (int i = 0; i <20 ; i++) {
                producer.clerk.get();
            }

        },"producer").start();

        new Thread(()->{

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //賣貨
            for (int i = 0; i <20 ; i++) {
                consumer.clerk.sale();
            }

        },"consumer").start();



    }
}


//店員
class Clerk{

    int product = 0;

    //進貨
    public synchronized void  get(){
        if (product >= 1) {
            System.out.println("產品已滿!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else {
            System.out.println(Thread.currentThread().getName()+" : " + ++product);

            this.notifyAll();
        }
    }

    //賣貨
    public  synchronized void sale(){
        if (product <= 0) {
            System.out.println("缺貨!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else {
            System.out.println(Thread.currentThread().getName()+" : " + --product);

            this.notifyAll();
        }
    }
}

//producer
class Producer{

    Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

}

class Consumer{

    Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

}

在這裏插入圖片描述

問題發生在:
	sale 和 get方法 裏面的 else那塊 

因爲:
	當product =0 
 public  synchronized void sale(){
        if (product <= 0) {
            System.out.println("缺貨!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else {
            System.out.println(Thread.currentThread().getName()+" : " + --product);

            this.notifyAll();
        }
    }

代碼運行在   this.wait(); 這
this.wait():	
	等在這個位置  同時釋放鎖的資源 
	當他被喚醒之後 從this.wait() 代碼的位置 往下繼續執行

那麼此時 生產者線程:
public synchronized void  get(){
        if (product >= 1) {
            System.out.println("產品已滿!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }else {
            System.out.println(Thread.currentThread().getName()+" : " + ++product);

            this.notifyAll();
        }
    }

代碼運行 else  ++product  導致product 變爲1 
然後 this.notifyAll();  釋放鎖 之後 

此時 開始生產者和消費值繼續搶佔cpu  如果 消費者 搶到!!
 消費者會 接着從 sale 方法裏面的  this.wait(); 往下執行 跳出else 就是消費者
  執行完畢ok了
 就剩下 生產者一個線程了 

接着 生產者 發現 product=1  會運行 
	System.out.println("產品已滿!");
	this.wait();   釋放鎖等待被 喚醒 !!!  但是 沒有消費者線程 了 
	所以不會被喚醒!!


所以代碼會卡住在 : 程序結束不了!!!
	產品已滿 或者 缺貨
解決:
	把else 去掉
package com.juc;

public class ProducerAndConsumerApp {
    public static void main(String[] args) {

        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);



        new Thread(()->{


            //生產貨物
            for (int i = 0; i <20 ; i++) {
                producer.clerk.get();
            }

        },"producer").start();

        new Thread(()->{

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //賣貨
            for (int i = 0; i <20 ; i++) {
                consumer.clerk.sale();
            }

        },"consumer").start();



    }
}


//店員
class Clerk{

    int product = 0;

    //進貨
    public synchronized void  get(){
        if (product >= 1) {
            System.out.println("產品已滿!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
            System.out.println(Thread.currentThread().getName()+" : " + ++product);

            this.notifyAll();
    }

    //賣貨
    public  synchronized void sale(){
        if (product <= 0) {
            System.out.println("缺貨!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

            System.out.println(Thread.currentThread().getName()+" : " + --product);

            this.notifyAll();
    }
}

//producer
class Producer{

    Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

}

class Consumer{

    Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

}


程序可以結束!!
還是有問題的!!:
	多個線程 就會有問題 
	上面是一個生產者一個消費值線程 是沒有問題的 
	那麼 兩個消費者 兩個生產者 就會有問題!!
package com.juc;

public class ProducerAndConsumerApp {
    public static void main(String[] args) {

        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);


        for (int j = 0; j < 2; j++) {

            new Thread(()->{


                //生產貨物
                for (int i = 0; i <20 ; i++) {
                    producer.clerk.get();
                }

            },"producer"+j).start();

            new Thread(()->{

//                try {
////                    Thread.sleep(2000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }

                //賣貨
                for (int i = 0; i <20 ; i++) {
                    consumer.clerk.sale();
                }

            },"consumer"+j).start();
        }




    }
}


//店員
class Clerk{

    int product = 0;

    //進貨
    public synchronized void  get(){
        if (product >= 1) {
            System.out.println("產品已滿!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
            System.out.println(Thread.currentThread().getName()+" : " + ++product);

            this.notifyAll();
    }

    //賣貨
    public  synchronized void sale(){
        if (product <= 0) {
            System.out.println("缺貨!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

            System.out.println(Thread.currentThread().getName()+" : " + --product);

            this.notifyAll();
    }
}

//producer
class Producer{

    Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

}

class Consumer{

    Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

}
結果:

producer0 : 1
產品已滿!
consumer0 : 0
缺貨!
producer1 : 1
產品已滿!
producer0 : 2
產品已滿!
producer1 : 3
產品已滿!
consumer0 : 2
consumer0 : 1
consumer0 : 0
缺貨!
缺貨!
producer1 : 1
產品已滿!
producer0 : 2
產品已滿!
producer1 : 3
產品已滿!
consumer1 : 2
consumer1 : 1
consumer1 : 0
缺貨!
consumer0 : -1
缺貨!
consumer1 : -2
缺貨!
producer1 : -1
producer1 : 0
producer1 : 1
產品已滿!
producer0 : 2
產品已滿!
producer1 : 3
產品已滿!
consumer1 : 2
consumer1 : 1
consumer1 : 0
缺貨!
consumer0 : -1
缺貨!
consumer1 : -2
缺貨!
producer1 : -1
producer1 : 0
producer1 : 1
產品已滿!
producer0 : 2
產品已滿!
producer1 : 3
產品已滿!
consumer1 : 2
consumer1 : 1
consumer1 : 0
缺貨!
consumer0 : -1
缺貨!
consumer1 : -2
缺貨!
producer1 : -1
producer1 : 0
producer1 : 1
產品已滿!
producer0 : 2
產品已滿!



都出現負數了!!

如何導致的:
	public  synchronized void sale(){
        if (product <= 0) {
            System.out.println("缺貨!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

            System.out.println(Thread.currentThread().getName()+" : " + --product);

            this.notifyAll();
    }

兩個線程 都進到這個方法 :
	都停在 this.wait();   如果 同時被 喚醒 
	product=0   --product之後 就出現負數了

這就是虛假喚醒問題!!!
官網的解釋是 :
	this.wait(); 必須使用在 while  
	所以要把 if 變成 while  爲什麼呢??
	就是 要再判斷一次!!! 就解決了!虛假喚醒問題!
	
package com.juc;

public class ProducerAndConsumerApp {
    public static void main(String[] args) {

        Clerk clerk = new Clerk();
        Producer producer = new Producer(clerk);
        Consumer consumer = new Consumer(clerk);


        for (int j = 0; j < 2; j++) {

            new Thread(()->{


                //生產貨物
                for (int i = 0; i <20 ; i++) {
                    producer.clerk.get();
                }

            },"producer"+j).start();

            new Thread(()->{

//                try {
////                    Thread.sleep(2000);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }

                //賣貨
                for (int i = 0; i <20 ; i++) {
                    consumer.clerk.sale();
                }

            },"consumer"+j).start();
        }




    }
}


//店員
class Clerk{

    int product = 0;

    //進貨
    public synchronized void  get(){
        while (product >= 1) {
            System.out.println("產品已滿!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
            System.out.println(Thread.currentThread().getName()+" : " + ++product);

            this.notifyAll();
    }

    //賣貨
    public  synchronized void sale(){
        while (product <= 0) {
            System.out.println("缺貨!");

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

            System.out.println(Thread.currentThread().getName()+" : " + --product);

            this.notifyAll();
    }
}

//producer
class Producer{

    Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

}

class Consumer{

    Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

}
那麼 以上是 使用 synchronized 和 wait + notify的方式 完成等待喚醒機制

如何使用lock完成等待喚醒機制呢???

lock 完成等待喚醒機制

wait 和 notify  
Lock 有自己的 
	叫 Condition  控制線程通信
		await、signal、signalAll

Condition 是通過特定的Lock獲取  newCondition

就可以使用等待喚醒機制了!!!
package com.juc;

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

public class ProducerAndConsumerLockApp {
    public static void main(String[] args) {

        Clerk01 clerk = new Clerk01();
        Producer01 producer = new Producer01(clerk);
        Consumer01 consumer = new Consumer01(clerk);


        for (int j = 0; j < 2; j++) {

            new Thread(()->{

                //生產貨物
                for (int i = 0; i <20 ; i++) {
                    producer.clerk.get();
                }

            },"producer"+j).start();

            new Thread(()->{

                //賣貨
                for (int i = 0; i <20 ; i++) {
                    consumer.clerk.sale();
                }

            },"consumer"+j).start();
        }




    }
}


//店員
class Clerk01 {

    int product = 0;


    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    //進貨
    public  void  get(){

        lock.lock();

       try{
           while (product >= 1) {
               System.out.println("產品已滿!");

               try {
                   condition.await();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }

           }
           System.out.println(Thread.currentThread().getName()+" : " + ++product);

           condition.signalAll();
       }finally {
           lock.unlock();
       }
    }

    //賣貨
    public   void sale(){

        lock.lock();

        try{

            while (product <= 0) {
                System.out.println("缺貨!");

                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

            System.out.println(Thread.currentThread().getName()+" : " + --product);

            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}

//producer
class Producer01 {

    Clerk01 clerk;

    public Producer01(Clerk01 clerk) {
        this.clerk = clerk;
    }

}

class Consumer01 {

    Clerk01 clerk;

    public Consumer01(Clerk01 clerk) {
        this.clerk = clerk;
    }

}

線程按序交替

eg:
	編寫一個程序,開啓3個線程,這三個線程的ID分別是A、B、C、每個線程
	將自己的ID在屏幕上打印10遍,要求輸出的結果必須按順序顯示。
		如:
			ABCABCABC。。。ABC依次遞歸
分析:
	1.對線程控制 需要 Lock
	2.對線程 按順序控制 就需要 Condition
package com.juc;

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

public class ABCAPP {

    public static void main(String[] args) {

        ABC abc = new ABC();

        new Thread(()->{

            for (int i = 0; i < 10; i++) {
                abc.printA(i);
            }

        },"A").start();

        new Thread(()->{

            for (int i = 0; i < 10; i++) {
                abc.printB(i);
            }

        },"B").start();

        new Thread(()->{

            for (int i = 0; i < 10; i++) {
                abc.printC(i);

                System.out.println("---------------------------");
            }

        },"C").start();

    }
}

class ABC{

    //當前正在執行線程的標記
    int number =1;

   Lock lock  = new ReentrantLock();
   Condition condition1 = lock.newCondition();
   Condition condition2 = lock.newCondition();
   Condition condition3 = lock.newCondition();

    /**
     *
     * @param totalprint  表示循環幾輪
     * @throws InterruptedException
     */
   public void printA(int totalprint)  {
       lock.lock();

       try{

           //1.判斷
           if(number !=1){

               condition1.await();
           }

           //2.打印
           for (int i = 0; i <10 ; i++) {
               System.out.println(Thread.currentThread().getName()+"\t" +i+"\t" +totalprint);
           }

           //3.喚醒
           number =2;
           condition2.signal();

       } catch (InterruptedException e) {
           e.printStackTrace();
       } finally {
           lock.unlock();
       }

   }


    public void printB(int totalprint)  {
        lock.lock();

        try{

            //1.判斷
            if(number !=2){
                condition2.await();
            }

            //2.打印
            for (int i = 0; i <10 ; i++) {
                System.out.println(Thread.currentThread().getName()+"\t" +i+"\t" +totalprint);
            }

            //3.喚醒
            number =3;
            condition3.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }


    public void printC(int totalprint)  {
        lock.lock();

        try{

            //1.判斷
            if(number !=3){
                condition3.await();
            }

            //2.打印
            for (int i = 0; i <10 ; i++) {
                System.out.println(Thread.currentThread().getName()+"\t" +i+"\t" +totalprint);
            }

            //3.喚醒
            number =1;
            condition1.signal();

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }


}
上面的代碼 :
	是 10輪 打印10次

結果:
A	0	0
A	1	0
A	2	0
A	3	0
A	4	0
A	5	0
A	6	0
A	7	0
A	8	0
A	9	0
B	0	0
B	1	0
B	2	0
B	3	0
B	4	0
B	5	0
B	6	0
B	7	0
B	8	0
B	9	0
C	0	0
C	1	0
C	2	0
C	3	0
C	4	0
C	5	0
C	6	0
C	7	0
C	8	0
C	9	0
---------------------------
A	0	1
A	1	1
A	2	1
A	3	1
A	4	1
A	5	1
A	6	1
A	7	1
A	8	1
A	9	1
B	0	1
B	1	1
B	2	1
B	3	1
B	4	1
B	5	1
B	6	1
B	7	1
B	8	1
B	9	1
C	0	1
C	1	1
C	2	1
C	3	1
C	4	1
C	5	1
C	6	1
C	7	1
C	8	1
C	9	1
---------------------------
A	0	2
A	1	2
A	2	2
A	3	2
A	4	2
A	5	2
A	6	2
A	7	2
A	8	2
A	9	2
B	0	2
B	1	2
B	2	2
B	3	2
B	4	2
B	5	2
B	6	2
B	7	2
B	8	2
B	9	2
C	0	2
C	1	2
C	2	2
C	3	2
C	4	2
C	5	2
C	6	2
C	7	2
C	8	2
C	9	2
---------------------------

ReadWriteLock讀寫鎖

就是讀寫分離:

什麼情況下使用呢?
	1.寫 多線程   就是 寫寫 要互斥
	2.讀寫   讀和寫 也要互斥
	3.讀讀 是不需要互斥的  
package com.juc;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteApp {

    public static void main(String[] args) {

        /**
         * 那麼一個線程去 寫 100 個線程去讀
         */
        Demo demo = new Demo();

        new Thread(()->{
            demo.set((int)(Math.random()*101));
        },"write").start();


        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                demo.get();
            },"read").start();
        }


    }
}


class Demo{

    int number = 0;
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    //讀
    public  void  get(){
        readWriteLock.readLock().lock();

        try{
            System.out.println(Thread.currentThread().getName()+" : " +number);

        }finally {

            readWriteLock.readLock().unlock();
        }
    }

    //寫
    public void  set(int number){

        readWriteLock.writeLock().lock();

        try{
            System.out.println(Thread.currentThread().getName());
            this.number = number;
        }finally {
            readWriteLock.writeLock().unlock();
        }

    }


}
結果:
write
read : 36
read : 36
read : 36
read : 36
read : 36
read : 36
。。。

一個線程再 寫 100個線程再 讀  這就是 讀寫鎖

線程八鎖

1.1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 * 
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 * 
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number.getTwo();
            }).start();
    }
}

class  Number{

    public synchronized void getOne(){
        System.out.println("one");
    }

    public synchronized void getTwo(){
        System.out.println("two");
    }
}
結果:
one
two
2. 新增 Thread.sleep 給 getone方法   
package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 *
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 *
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number.getTwo();
            }).start();
    }
}

class  Number{

    public synchronized void getOne(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo(){
        System.out.println("two");
    }
}



結果:
one
two

3.新增 getThree 普通方法 

package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 *
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 *
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number.getTwo();
            }).start();

        new Thread(()->{

            number.getThree();
        }).start();
    }
}

class  Number{

    public synchronized void getOne(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo(){
        System.out.println("two");
    }

    public void getThree(){
        System.out.println("three");
    }
}

結果:
three
one
two
4.兩個普通同步方法 兩個Number對象  打印?

package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 *
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 *
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();
        Number number2 = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number2.getTwo();
            }).start();

//        new Thread(()->{
//
//            number.getThree();
//        }).start();
    }
}

class  Number{

    public synchronized void getOne(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo(){
        System.out.println("two");
    }

//    public void getThree(){
//        System.out.println("three");
//    }
}

結果:
two
one
5.修改 getOne爲 靜態的同步方法 打印? two one

package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 *
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 *
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();
//        Number number2 = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number.getTwo();
            }).start();

//        new Thread(()->{
//
//            number.getThree();
//        }).start();
    }
}

class  Number{

    public static synchronized void getOne(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public synchronized void getTwo(){
        System.out.println("two");
    }

//    public void getThree(){
//        System.out.println("three");
//    }
}


結果:
two
one
6. 修改兩個同步方法都爲靜態方法 

package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 *
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 *
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();
//        Number number2 = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number.getTwo();
            }).start();

//        new Thread(()->{
//
//            number.getThree();
//        }).start();
    }
}

class  Number{

    public static synchronized void getOne(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public static synchronized void getTwo(){
        System.out.println("two");
    }

//    public void getThree(){
//        System.out.println("three");
//    }
}

結果:
one
two
7.  一個靜態同步方法 一個非靜態同步方法 兩個Number對象
package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 *
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 *
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();
        Number number2 = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number2.getTwo();
            }).start();

//        new Thread(()->{
//
//            number.getThree();
//        }).start();
    }
}

class  Number{

    public static synchronized void getOne(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public  synchronized void getTwo(){
        System.out.println("two");
    }

//    public void getThree(){
//        System.out.println("three");
//    }
}

結果:
two
one
8.兩個靜態同步方法  兩個Number對象
package com.juc;

/**
 * 判斷 打印的是 one 還是 two
 *
 * 1.兩個普通同步方法 兩個線程 標準打印 ,  //one two
 *
 */
public class Thread8Monitor {

    public static void main(String[] args) {

        Number number = new Number();
        Number number2 = new Number();


            new Thread(()->{
                number.getOne();
            }).start();

            new Thread(()->{

                number2.getTwo();
            }).start();

//        new Thread(()->{
//
//            number.getThree();
//        }).start();
    }
}

class  Number{

    public static synchronized void getOne(){
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }

    public  static synchronized void getTwo(){
        System.out.println("two");
    }

//    public void getThree(){
//        System.out.println("three");
//    }
}

結果:
one
two
總結:
	線程8鎖 關鍵:
		1.非靜態方法的鎖 默認爲 this  靜態方法的鎖 對應的 Class 實例 xxx.class
		2.某一個時刻內 只能有一個線程持有鎖 無論幾個方法

線程池

爲什麼使用線程池呢??
	之前使用線程 是 new Thread 的方式 
	這樣是 需要就創建一個線程 
	實際上就是 創建線程  然後銷燬線程 
	如果頻繁的創建和銷燬的話,
	那麼也是非常耗費資源的。

那麼 你一定使用過數據庫的連接池 。
爲什麼你使用它呢?
	性能好呀
	爲什麼好呢?
		1.不是頻繁的創建和銷燬
		2.是去 線程池裏面 取連接就好 用完放回去 (池子裏 先準備好 一些連接)
線程池:1.5之後   線程的使用和調度的 接口 是 EXecutor 
	1.提供了一個線程隊列  存的是 所有等待狀態的線程
	 避免創建和銷燬的開銷 提高效率
	 2.體系結構:
	 	 1.Executor : 使用+調度
	 	 2.ExecutorService :線程池的主要接口
	 		 3.ThreadPoolExecutor : 線程池的實現類
	 	 	 4.ScheduledExecutorSerivce : 線程的調度 
	 	 		5.ScheduledThreadPollExecutor:實現了4 繼承了3   
	 	 				具備線程池和線程的調度的功能
	3.工具類:Executors   提供了很多方法!!  
		eg:
			newFixedThreadPoll()  創建固定大小的線程池
			newCachedThreadPoll() 線程池的數量不固定 可以根據需求自動更改數量
			newSingleThreadExecutor() 創建單個線程池 線程池裏只有一個線程
			newScheduledThreadPoll() 創建固定大小的線程 可以延遲定時 的執行任務			 	 
package com.juc;

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

public class ThreadPollApp {

    public static void main(String[] args) {


        Poll poll = new Poll();

       //1.創建線程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        //2.爲線程池中的線程  分配任務
        executorService.submit(new Thread(()->{

            while (poll.i < 100) {
                System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
            }
        }));


        //3.關閉線程池
        /**
         * shutdown :
         *     等待 現有的線程池中的 線程任務完成之後 再關閉 來新的資源 也不接受了
         *
         * shutdownNow:
         *    立即關閉
         */
        executorService.shutdown();

    }
}

class Poll{
    int i =0;

}
結果:
pool-1-thread-1 : 1
pool-1-thread-1 : 2
pool-1-thread-1 : 3
pool-1-thread-1 : 4
pool-1-thread-1 : 5
pool-1-thread-1 : 6
pool-1-thread-1 : 7
pool-1-thread-1 : 8
pool-1-thread-1 : 9
pool-1-thread-1 : 10
pool-1-thread-1 : 11
pool-1-thread-1 : 12
pool-1-thread-1 : 13
pool-1-thread-1 : 14
pool-1-thread-1 : 15
pool-1-thread-1 : 16
pool-1-thread-1 : 17
pool-1-thread-1 : 18
pool-1-thread-1 : 19
。。。


 executorService.submit(new Thread(()->{

            while (poll.i < 100) {
                System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
            }
        }));

只有一個一個線程 分配任務 
package com.juc;

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

public class ThreadPollApp {

    public static void main(String[] args) {


        Poll poll = new Poll();

       //1.創建線程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        //2.爲線程池中的線程  分配任務
        for (int i = 0; i < 10; i++) {

            executorService.submit(new Thread(()->{

                while (poll.i < 100) {
                    System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
                }
            }));
        }
        
        //3.關閉線程池
        /**
         * shutdown :
         *     等待 現有的線程池中的 線程任務完成之後 再關閉 來新的資源 也不接受了
         *
         * shutdownNow:
         *    立即關閉
         */
        executorService.shutdown();

    }
}

class Poll{
    int i =0;

}
結果:
pool-1-thread-2 : 1
pool-1-thread-4 : 3
pool-1-thread-4 : 5
pool-1-thread-4 : 6
pool-1-thread-4 : 8
pool-1-thread-4 : 9
pool-1-thread-3 : 2
pool-1-thread-3 : 11
pool-1-thread-3 : 12
pool-1-thread-3 : 13
pool-1-thread-3 : 14
pool-1-thread-3 : 15
pool-1-thread-3 : 16
pool-1-thread-3 : 17
pool-1-thread-3 : 18
pool-1-thread-3 : 19
pool-1-thread-3 : 20
pool-1-thread-3 : 21
pool-1-thread-3 : 22
pool-1-thread-3 : 23
pool-1-thread-3 : 24
pool-1-thread-3 : 25
pool-1-thread-3 : 26
pool-1-thread-3 : 27
pool-1-thread-3 : 28
pool-1-thread-3 : 29
pool-1-thread-1 : 0
pool-1-thread-1 : 31
pool-1-thread-1 : 32
pool-1-thread-1 : 33
pool-1-thread-1 : 34
pool-1-thread-1 : 35
pool-1-thread-1 : 36
pool-1-thread-1 : 37
pool-1-thread-1 : 38
pool-1-thread-1 : 39
pool-1-thread-1 : 40
pool-1-thread-3 : 30
pool-1-thread-3 : 42
pool-1-thread-3 : 43
pool-1-thread-3 : 44
pool-1-thread-3 : 45
pool-1-thread-3 : 46
pool-1-thread-4 : 10
pool-1-thread-4 : 48
pool-1-thread-4 : 49
pool-1-thread-4 : 50
pool-1-thread-5 : 7
pool-1-thread-5 : 52
pool-1-thread-2 : 4
pool-1-thread-2 : 54
pool-1-thread-2 : 55
pool-1-thread-2 : 56
pool-1-thread-2 : 57
pool-1-thread-2 : 58
pool-1-thread-2 : 59
pool-1-thread-2 : 60
pool-1-thread-2 : 61
pool-1-thread-2 : 62
pool-1-thread-2 : 63
pool-1-thread-2 : 64
pool-1-thread-2 : 65
pool-1-thread-2 : 66
pool-1-thread-2 : 67
pool-1-thread-5 : 53
pool-1-thread-5 : 69
pool-1-thread-4 : 51
pool-1-thread-4 : 71
pool-1-thread-4 : 72
pool-1-thread-3 : 47
pool-1-thread-3 : 74
pool-1-thread-3 : 75
pool-1-thread-3 : 76
pool-1-thread-3 : 77
pool-1-thread-3 : 78
pool-1-thread-3 : 79
pool-1-thread-1 : 41
pool-1-thread-1 : 81
pool-1-thread-1 : 82
pool-1-thread-1 : 83
pool-1-thread-1 : 84
pool-1-thread-1 : 85
pool-1-thread-1 : 86
pool-1-thread-1 : 87
pool-1-thread-1 : 88
pool-1-thread-1 : 89
pool-1-thread-1 : 90
pool-1-thread-1 : 91
pool-1-thread-3 : 80
pool-1-thread-4 : 73
pool-1-thread-5 : 70
pool-1-thread-2 : 68
pool-1-thread-5 : 95
pool-1-thread-4 : 94
pool-1-thread-3 : 93
pool-1-thread-1 : 92
pool-1-thread-3 : 99
pool-1-thread-4 : 98
pool-1-thread-5 : 97
pool-1-thread-2 : 96
pool-1-thread-5:
	池1:   pool-1
	線程5: thread-5

明白了嗎?
	線程池裏面 5個線程   
	我分配了 10個任務 
callable:

package com.juc;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class ThreadPollApp {

    public static void main(String[] args) throws ExecutionException, InterruptedException {


        Poll poll = new Poll();

       //1.創建線程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);


        List<Future<Integer>> list = new ArrayList<>();

        //2.爲線程池中的線程  分配任務

        for (int j = 0; j < 10; j++) {

            Future<Integer> future = executorService.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {

                    int sum = 0;

                    for (int i = 0; i < 100; i++) {
                        sum += i;
                    }

                    return sum;
                }
            });

            list.add(future);

        }


        //遍歷10個任務的結果
        for (Future<Integer>  future: list){

            System.out.println(future.get());

        }


        //2.爲線程池中的線程  分配任務
//        for (int i = 0; i < 10; i++) {
//
//            executorService.submit(new Thread(()->{
//
//                while (poll.i < 100) {
//                    System.out.println(Thread.currentThread().getName()+" : " + poll.i++);
//                }
//            }));
//        }

        //3.關閉線程池
        /**
         * shutdown :
         *     等待 現有的線程池中的 線程任務完成之後 再關閉 來新的資源 也不接受了
         *
         * shutdownNow:
         *    立即關閉
         */
        executorService.shutdown();

    }
}

class Poll{
    int i =0;

}
結果:
4950
4950
4950
4950
4950
4950
4950
4950
4950
4950

線程池+調度

package com.juc;

import java.util.concurrent.*;

public class ScheduledThreadPoolApp {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);


        for (int i = 0; i < 10; i++) {
            Future<Integer> future = pool.schedule(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {

                    int num = (int) (Math.random() * 101);
                    System.out.println(Thread.currentThread().getName() + " : " + num);
                    return num;
                }
            }, 1, TimeUnit.SECONDS);


            System.out.println(future.get());

        }


        pool.shutdown();

    }
}


結果:
pool-1-thread-1 : 12
12
pool-1-thread-1 : 69
69
pool-1-thread-2 : 39
39
pool-1-thread-1 : 28
28
pool-1-thread-3 : 79
79
pool-1-thread-2 : 17
17
pool-1-thread-4 : 78
78
pool-1-thread-1 : 5
5
pool-1-thread-5 : 62
62
pool-1-thread-3 : 32
32

ForkJoinPool 分支/合併

思想:
	1.任務 遞歸分配成若干 小任務(拆到不可再拆)
	2.小任務 並行求值
	3.結果合併

在這裏插入圖片描述

工作竊取:
	之前線程池:
		1.4個內核  
			1號和4號 假如發生線程阻塞
			2號和3號 執行完任務  
			那麼 23是空閒狀態
			cpu沒有更加合理的利用 
jdk1.7之後 forkjoin:
	工作竊取模式 :
		把大任務拆分成小任務 ,把小任務 分配到不同的線程中
		形成一個線程隊列(裏面是小任務)是雙端隊列  就意味着 
		一旦某一個線程 獲取任務的時候 獲取不到時候 會發生阻塞 避免阻塞發生 
		這個線程 會去別的線程 隊列的末尾 偷一個任務進行執行!!

更好的利用cpu資源 效率高 !!
	cpu幾個 就有用幾個線程

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