Java並發編程--基礎進階高級(完結)


Java並發編程--基礎進階高級完整筆記。

這都不知道是第幾次刷狂神的JUC並發編程了,從第一次的迷茫到現在比較清晰,算是個大進步了,之前JUC筆記不見了,重新做一套筆記。

參考鏈接:https://www.bilibili.com/video/BV1B7411L7tE


csdn csdn csdn csdn csdn




🔥1.多線程--基礎內容

1.Thread狀態

​ 6種:新建、運行、阻塞、等待、超時等待、結束(可點擊Thread查看源代碼)

public enum State {
        NEW,

        RUNNABLE,

        BLOCKED,

        WAITING,

        TIMED_WAITING,

        TERMINATED;
    }

2.Synchronized
  • 非公平鎖
  • 可重入鎖,

​ Synchronized:是非公平鎖(不能保證線程獲得鎖的順序,即線程不會依次排隊去獲取資源,而是爭搶,但是結果一定是正確的),是可重入鎖(已獲得一個鎖,可以再獲得鎖且不會造成死鎖,比如synchronized內部可以再寫個synchronized函數)

    /**
     * Author: HuYuQiao
     * Description: Synchronized實現方式(修飾函數即可)
     */
    class TicketSync{
        public int number = 50;
        //synchronized本質是隊列,鎖
        public synchronized void sale(){
            if(number > 0) {
                System.out.println(Thread.currentThread().getName() + "獲得了第" + number-- +"票");
            }
        }
    }

3.Lock鎖
  • 可重入鎖

  • 公平還是不公平鎖可以設置(默認不公平鎖)

        /**
         * Creates an instance of {@code ReentrantLock} with the
         * given fairness policy.
         *
         * @param fair {@code true} if this lock should use a fair ordering policy
         */
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    

    Lock:加鎖之后必須解鎖,,否則其他線程就獲取不到了,所以用try-catch-finally包起來。

    /**
     * Author: HuYuQiao
     * Description: Lock實現方式(加鎖、解鎖)
     */
    class TicketLock{
        Lock lock = new ReentrantLock();
        public int number = 50;
        public void sale(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "獲得了第" + number-- +"票");

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

4.總結

​ 在不加鎖情況下,多線程會爭搶,導致輸出順序、計算結果都會不一致(上面例子結果如果一樣是因為只有3個線程,for循環即出錯,因為number--這個函數本身不是線程安全的),所以就引入了鎖的概念,synchronized,lock保證了輸出順序、計算結果的一致性。

虛假喚醒:在synchronized.wait與lock.condition.await喚醒線程時,是從await代碼之后開始運行,所以為了保證能喚醒線程,需要用while語句將代碼包含起來。

​ 完整代碼

package com.empirefree.springboot;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import sun.security.krb5.internal.Ticket;

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

/**
 * @program: springboot
 * @description: 多線程
 * @author: huyuqiao
 * @create: 2021/06/26 14:26
 */

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ThreadTest {
    
    /**
     * Author: HuYuQiao
     * Description: Synchronized實現方式(修飾函數即可)
     */
    class TicketSync{
        public int number = 50;
        //synchronized本質是隊列,鎖
        public synchronized void sale(){
            System.out.println(Thread.currentThread().getName() + "獲得了第" + number-- +"票");

        }
    }
    
    /**
     * Author: HuYuQiao
     * Description: Lock實現方式(加鎖、解鎖)
     */
    class TicketLock{
        Lock lock = new ReentrantLock();
        public int number = 50;
        public void sale(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "獲得了第" + number-- +"票");

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

    @Test
    public void testThread() {
//        TicketSync ticket = new TicketSync();
        TicketLock ticket = new TicketLock();
        new Thread( () ->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }

        },"ThreadA").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }
        },"ThreadB").start();
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                ticket.sale();
            }
        },"ThreadC").start();

        for (int i = 0; i < 500; i++) {
            new Thread(() -> {
                ticket.sale();
            }).start();
        }
    }
}



🔥2.八鎖現象(synchronized、static)

即synchronized、static修飾的函數,執行順序、輸出結果。

結果表明:

​ 1.synchronized修飾的函數:會鎖住對象(可以看成鎖對象中某個方法),看起來代碼會依次執行,而沒有鎖的方法即不受影響,一來就先執行

​ 2.static synchronized修飾的函數:會鎖住類.class(可以不同對象訪問的都是同一個函數),所以2個對象訪問自己的函數依然還是順序執行.

​ 3.一個有static,一個沒有static:即一個鎖類.class,另一個鎖對象,不管是同一個對象還是不同對象,就都不需要等待了,不會順序執行。

1.synchronized

​ 修飾函數會保證同一對象依次順序執行()

class Phone{
    //synchronized
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendSms");
    }
    public synchronized  void call() {
        System.out.println("call");
    }

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

    public static void main(String[] args) {
        //Thread--代碼執行順序問題
        Phone phone = new Phone();
        new Thread(phone::sendSms, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(phone::call, "B").start();
        new Thread(phone::playGame, "C").start();
    }

2.static synchronized
class PhoneStatic{
    //static synchronized
    public static synchronized void sendSmsStatic() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sendSmsStatic");
    }
    public static synchronized  void callStatic() {
        System.out.println("callStatic");
    }
}

    public static void main(String[] args) {
        PhoneStatic phoneStatic = new PhoneStatic();
        PhoneStatic phoneStatic2 = new PhoneStatic();
        new Thread(() ->{
            phoneStatic2.sendSmsStatic();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() ->{
            phoneStatic2.callStatic();
        }, "B").start();
    }



🔥3.Java集合--安全性

        //集合安全性--list,set,map都非線程安全
        List<String> list = new Vector<>();
        List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();

        Map<String, String> objectObjectHashMap = new ConcurrentHashMap<>();
        Map<Object, Object> objectObjectHashMap1 = Collections.synchronizedMap(new HashMap<>());
        //set底層就是map:無論hashset還是linkedhashset
        Set<String> set = Collections.synchronizedSet(new LinkedHashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();   



🔥4.高並發--輔助類

​ 學習鏈接:https://www.cnblogs.com/meditation5201314/p/14395972.html

1.countdownLatch

Countdownlatch:減一操作,直到為0再繼續向下執行

package Kuangshen.JUC.Thread;

import java.util.concurrent.CountDownLatch;

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

        final CountDownLatch countDownLatch = new CountDownLatch(5);

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "get out");
                countDownLatch.countDown();
                }, String.valueOf(i)).start();
        }
        countDownLatch.await(); //等待上述執行完畢再向下執行
        System.out.println("close door");
    }
}

2.cyclicbarrier

Cyclicbarrier:+1操作,對於每個線程都自動+1並等待,累計到規定值再向下執行,

package Kuangshen.JUC.Thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author :Empirefree
 * @description:TODO
 * @date :2021/2/10 10:56
 */
public class cyclicbarrier {
    public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
        //CyclicBarrier里面是容量 + runnable
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () ->{
            System.out.println("不斷增加到7即向后執行,與countdownlatch相反");
        }
       );

        /*對於指派的局部變量,lambda只能捕獲一次 ,故而需定義成final(int內部定義就是final),而且線程中,
        不能對局部變量進行修改,如需要修改,需定義成原子類atomic
        */
        for (int i = 0; i < 7; i++) {
            int finalI = i;
            new Thread(() ->{
                System.out.println(finalI);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            }).start();
        }
    }

}

3.semaphore

semaphore:對於規定的值,多個線程只規定有指定的值能獲取,每次獲取都需要最終釋放,保證一定能互斥執行

package Kuangshen.JUC.Thread;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author :Empirefree
 * @description:TODO
 * @date :2021/2/10 15:24
 */
public class semaphore {
    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 60; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "搶到車位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "離開車位");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}



🔥5.讀寫鎖(ReadWriteLock)

​ ReadWriteLock也是多線程下的一種加鎖方式,下面列出ReadWriteLock和synchronized對多線程下,保證讀完之后在寫的實現方式

//讀寫鎖 :寫只有一個線程寫,寫完畢后 可以多個線程讀
MyCache myCache = new MyCache();
int num = 6;
for (int i = 1; i < num; i++) {
    int finalI = i;
    new Thread(()->{
        myCache.write(String.valueOf(finalI),String.valueOf(finalI));

    },String.valueOf(i)).start();
}
for (int i = 1; i < num; i++) {
    int finalI = i;
    new Thread(()->{
        myCache.read(String.valueOf(finalI));
    },String.valueOf(i)).start();
}

class MyCache{
    private volatile Map<String,String> map = new HashMap<>();
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    //存,寫
    public void write(String key,String value){
        lock.writeLock().lock();    //寫鎖
        try {

            System.out.println(Thread.currentThread().getName()+"線程開始寫入");
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"線程開始寫入ok");
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }
    //取,讀
    public void read(String key){
        lock.readLock().lock();     //讀鎖
        try {

            System.out.println(Thread.currentThread().getName()+"線程開始讀取");
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"線程讀取ok");
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }


    //存,寫
    public synchronized void writeSync(String key,String value){
        try {
            System.out.println(Thread.currentThread().getName()+"線程開始寫入");
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"線程開始寫入ok");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
    //取,讀
    public void readSync(String key){
        try {
            System.out.println(Thread.currentThread().getName()+"線程開始讀取");
            map.get(key);
            System.out.println(Thread.currentThread().getName()+"線程讀取ok");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}



🔥6.線程池

1.集合--隊列(阻塞隊列、同步隊列)

  • 阻塞隊列:blockingQueue(超時等待--拋棄,所以最后size=1)

            //阻塞隊列
            ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);
            arrayBlockingQueue.offer("a", 2, TimeUnit.SECONDS);
            arrayBlockingQueue.offer("a", 2, TimeUnit.SECONDS);
            System.out.println("超時等待==" + arrayBlockingQueue.size());
    
  • 同步隊列:synchronizeQueue(類似於生產者消費者)

            //同步隊列
            SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"put 01");
                    synchronousQueue.put("1");
                    System.out.println(Thread.currentThread().getName()+"put 02");
                    synchronousQueue.put("2");
                    System.out.println(Thread.currentThread().getName()+"put 03");
                    synchronousQueue.put("3");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take());
                    System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take());
                    System.out.println(Thread.currentThread().getName()+"take"+synchronousQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    

2.線程池基本概念(三大方法、七大參數、四種拒絕策略)
  • 三大方法:newSingleThreadExecutro(單個線程),newFixedThreadPool(固定大小線程池),newCachedThreadPool(可伸縮)

     ExecutorService threadPool = Executors.newSingleThreadExecutor();
            ExecutorService threadPool2 = Executors.newFixedThreadPool(5);
            ExecutorService threadPool3 = Executors.newCachedThreadPool();
    
  • 七大參數:

    ThreadPoolExecutor(int corePoolSize,  //核心線程池大小
                              int maximumPoolSize, //最大的線程池大小(當阻塞隊列滿了就會打開)
                              long keepAliveTime,  //(空閑線程最大存活時間:即最大線程池中有空閑線程超過這個時間就會釋放線程,避免資源浪費)
                              TimeUnit unit, //超時單位
                              BlockingQueue<Runnable> workQueue, //阻塞隊列
                              ThreadFactory threadFactory, //線程工廠 創建線程的 一般不用動
                              RejectedExecutionHandler handler //拒絕策略
    
    
  • 四種拒絕策略:

    new ThreadPoolExecutor.AbortPolicy: // 該 拒絕策略為:銀行滿了,還有人進來,不處理這個人的,並拋出異常
    new ThreadPoolExecutor.CallerRunsPolicy(): // //該拒絕策略為:哪來的去哪里 main線程進行處理
    new ThreadPoolExecutor.DiscardPolicy(): //該拒絕策略為:隊列滿了,丟掉異常,不會拋出異常。
    new ThreadPoolExecutor.DiscardOldestPolicy(): //該拒絕策略為:隊列滿了,嘗試去和最早的進程競爭,不會拋出異常
    



🔥7.Stream(4個函數式接口、Lambda、異步回調)

1.函數式接口

​ 學習鏈接:https://www.cnblogs.com/meditation5201314/p/13693089.html


2.Lambda表達式

​ 學習鏈接:https://www.cnblogs.com/meditation5201314/p/13651755.html


3.異步回調
        //異步回調--無返回值
        CompletableFuture<Void> future = CompletableFuture.runAsync(() ->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "....");
        });
        System.out.println("begin");
        System.out.println(future.get());
        System.out.println("end");

        //異步回調--有返回值
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() ->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 3;
        });
        System.out.println("begin");
        System.out.println(future2.get());
        System.out.println(future2.whenComplete((result, error) ->{
            System.out.println("返回結果:" + result);
            System.out.println("錯誤結果" + error);
        }).exceptionally(throwable -> {
            System.out.println(throwable.getMessage());
            return 502;
        }).get());
        System.out.println("end");

        CompletableFuture<Integer> future = future2.whenComplete((result, error) ->{
            System.out.println("返回結果:" + result);
            System.out.println("錯誤結果" + error);
        }).handle((result, error)->{
            if (!ObjectUtils.isEmpty(result)){
                System.out.println(result + "handle result");
                return 1234;
            }
            if (!ObjectUtils.isEmpty(error)){
                System.out.println(error + "error");
            }
            return 0;
        });



🔥8.單例模式

1.餓漢模式(程序一啟動就new,十分占內存)
/**
 * @program: untitled
 * @description: 單例模式
 * @author: huyuqiao
 * @create: 2021/06/27 14:44
 */

public class SingleModel {

    /*
     * 可能會浪費空間
     * */
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];
    private static final SingleModel hugrySingle = new SingleModel();
    private SingleModel(){

    }
    public static SingleModel getInstance(){
        return hugrySingle;
    }
}


2.懶漢模式(DCL模式:雙重檢測,需要的時候才new)

1.第一層if沒有加鎖,所以會有多個線程到達if
2.如果內部new對象指令重排,就會導致有些線程認為lazyManModel有對象,所以會直接返回lazyManModel(實際為null)
3.所以需要加上valitile防止指令重排

/**
 * @program: untitled
 * @description: 懶漢式
 * @author: huyuqiao
 * @create: 2021/06/27 15:06
 */

public class LazyManModel {
    private volatile static LazyManModel lazyManModel;
    private LazyManModel(){
        System.out.println(Thread.currentThread().getName() + "...");
    }

    //DCL懶漢式:雙重檢測鎖--實現效果,只有為空的才null,否則不用null,所以需要2重if判斷。
    public static LazyManModel getInstance(){
        if (lazyManModel == null){
            synchronized (LazyManModel.class){
                if (lazyManModel == null){
                    lazyManModel = new LazyManModel();
                }
            }
        }
        return lazyManModel;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() ->{
                LazyManModel.getInstance();
            }).start();
        }
    }
}



🔥9.Volatile和Atomic

​ 學習筆記:https://www.cnblogs.com/meditation5201314/p/13707590.html

🔥10.Java中鎖

1.公平鎖(FIFO):Lock鎖可以自定義,synchronized
2.非公平鎖(允許插隊):Lock鎖可以自定義
3.可重入鎖(獲取一個鎖再獲取其他鎖不會造成死鎖):lock鎖和synchronized
4.自旋鎖:得不到就一直等待(Atomic.getAndIncrement底層就是自旋鎖)
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @program: untitled
 * @description: spinlock
 * @author: huyuqiao
 * @create: 2021/06/27 15:40
 */

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

        //使用CAS實現自旋鎖
        SpinlockDemo spinlockDemo=new SpinlockDemo();
        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"t1").start();

        TimeUnit.SECONDS.sleep(1);


        new Thread(()->{
            spinlockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                spinlockDemo.myUnlock();
            }
        },"t2").start();
    }
}

class SpinlockDemo {

    // 默認
    // int 0
    //thread null
    AtomicReference<Thread> atomicReference=new AtomicReference<>();

    //加鎖
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"===> mylock");

        //自旋鎖--為空則返回true,否則返回false
        while (!atomicReference.compareAndSet(null,thread)){
            System.out.println(Thread.currentThread().getName()+" ==> .自旋中~");
        }
    }


    //解鎖
    public void myUnlock(){
        Thread thread=Thread.currentThread();
        System.out.println(thread.getName()+"===> myUnlock");
        atomicReference.compareAndSet(thread,null);
    }

}

5.死鎖命令排查
jps -l
jstack 進程號


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM