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");
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 進程號