Java併發程式設計--基礎進階高階完整筆記。

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

參考連結:https://www.bilibili.com/video/BV1B7411L7tE






目錄

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");

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 程序號