JUC-狂神筆記整理學習


什么是JUC

 

 

在Java中,線程部分是一個重點,本篇文章說的JUC也是關於線程的。JUC就是java.util .concurrent工具包的簡稱。這是一個處理線程的工具包,JDK 1.5開始出現的。

 

知識點不錯的博客參看這里:https://www.jianshu.com/p/1f19835e05c0

本文資料來自Up主,狂神,記錄下來方便復

 
業務:普通的線程代碼 Thread
Runnable 沒有返回值、效率相比入 Callable 相對較低!

 

並發編程:並發、並行

並發(多線程操作同一個資源)
CPU 一核 ,模擬出來多條線程,天下武功,唯快不破,快速交替
並行(多個人一起行走)
CPU 多核 ,多個線程可以同時執行; 線程池
並發編程的本質:充分利用CPU的資源
 

線程六個狀態

public enum State {
// 新生
NEW,
// 運行
RUNNABLE,
// 阻塞
BLOCKED
// 等待,死死地等 
WAITING, 
// 超時等待 
TIMED_WAITING,
// 終止 
TERMINATED; }
 
.

Java默認有幾個線程? 2 個 mian、GC

線程:開了一個進程 Typora,寫字,自動保存(線程負責的)
對於Java而言:Thread、Runnable、Callable
Java 真的可以開啟線程嗎? 開不了
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// 本地方法,底層的C++ ,Java 無法直接操作硬件
private native void start0();

 

 
CPU 多核 ,多個線程可以同時執行; 線程池
 
public class Test1 {
public static void main(String[] args) {
// 獲取cpu的核數
// CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}

wait/sleep 區別

1、來自不同的類
wait => Object
sleep => Thread
2、關於鎖的釋放
wait 會釋放鎖,sleep 睡覺了,抱着鎖睡覺,不會釋放!
3、使用的范圍是不同的
wait必須在同步在代碼塊中
sleep 可以再任何地方睡
4、是否需要捕獲異常
wait 不需要捕獲異常
sleep 必須要捕獲異常

Lock鎖(重點)

傳統 Synchronized
// 運行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死死地等
WAITING,
// 超時等待
TIMED_WAITING,
// 終止
TERMINATED;
}
// 基本的賣票例子
 

Lock 接口公平鎖:十分公平:可以先來后到

非公平鎖:十分不公平:可以插隊 (默認)

 

// Lock三部曲
 // 1、 new ReentrantLock(); 
// 2、 lock.lock(); // 加鎖 
// 3、 finally=> lock.unlock(); // 解鎖 
class Ticket2 { 
// 屬性、方法 
private int number = 30;
 Lock lock = new ReentrantLock();
 public void sale(){
 lock.lock(); // 加鎖 
try {
// 業務代碼
 if (number>0)
{ 
System.out.println(Thread.currentThread().getName()+"賣出了"+ (number--)+"票,剩余:"+number); } }
 catch (Exception e)
 { e.printStackTrace(); }
 finally { lock.unlock(); // 解鎖 } }

 

}Synchronized 和 Lock 區別

1、Synchronized 內置的Java關鍵字, Lock 是一個Java類
2、Synchronized 無法判斷獲取鎖的狀態,Lock 可以判斷是否獲取到了鎖
3、Synchronized 會自動釋放鎖,lock 必須要手動釋放鎖!如果不釋放鎖,死鎖
4、Synchronized 線程 1(獲得鎖,阻塞)、線程2(等待,傻傻的等);Lock鎖就不一定會等待下
去;
5、Synchronized 可重入鎖,不可以中斷的,非公平;Lock ,可重入鎖,可以 判斷鎖,非公平(可以
自己設置);
6、Synchronized 適合鎖少量的代碼同步問題,Lock 適合鎖大量的同步代碼!
鎖是什么,如何判斷鎖的是誰!

生產者和消費者問題

面試的:單例模式、排序算法、生產者和消費者、死鎖
 
生產者和消費者問題 Synchronized 版
/**
* 線程之間的通信問題:生產者和消費者問題! 等待喚醒,通知喚醒
* 線程交替執行 A B 操作同一個變量 num = 0
* A num+1
* B num-1
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
// 判斷等待,業務,通知
class Data{ // 數字 資源類
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (number!=0){ //0
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線程,我+1完畢了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number==0){ // 1// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線程,我-1完畢了
this.notifyAll();
}
}
問題存在,A B C D 4 個線程! 虛假喚醒
if 改為 while 判斷
package com.kuang.pc;
/**
* 線程之間的通信問題:生產者和消費者問題! 等待喚醒,通知喚醒
* 線程交替執行 A B 操作同一個變量 num = 0
* A num+1
* B num-1
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判斷等待,業務,通知
class Data{ // 數字 資源類
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (number!=0){ //0
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線程,我+1完畢了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number==0){ // 1
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線程,我-1完畢了
this.notifyAll();
}
}

 

JUC版的生產者和消費者問題

 

 

通過Lock 找到 Condition
代碼實現:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B {
public static void main(String[] args) {Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判斷等待,業務,通知
class Data2{ // 數字 資源類
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await(); // 等待
//condition.signalAll(); // 喚醒全部
//+1
public void increment() throws InterruptedException {lock.lock();
try {
// 業務代碼
while (number!=0){ //0
// 等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線程,我+1完畢了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public synchronized void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){ // 1
// 等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線程,我-1完畢了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

 

 

Condition 精准的通知和喚醒線程.

代碼測試:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* A 執行完調用B,B執行完調用C,C執行完調用A
*/
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{for (int i = 0; i <10 ; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{ // 資源類 Lock
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; // 1A 2B 3C
public void printA(){
lock.lock();
try {
// 業務,判斷-> 執行-> 通知
while (number!=1){
// 等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
// 喚醒,喚醒指定的人,B
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
// 業務,判斷-> 執行-> 通知
while (number!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
// 喚醒,喚醒指定的人,c
number = 3;
condition3.signal();5、8鎖現象
如何判斷鎖的是誰!永遠的知道什么鎖,鎖到底鎖的是誰!
深刻理解我們的鎖
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
// 業務,判斷-> 執行-> 通知
// 業務,判斷-> 執行-> 通知
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
// 喚醒,喚醒指定的人,c
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

 

關於鎖的8個問題(引用狂神的代碼)

package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 8鎖,就是關於鎖的8個問題
* 1、標准情況下,兩個線程先打印 發短信還是 打電話? 1/發短信 2/打電話
* 1、sendSms延遲4秒,兩個線程先打印 發短信還是 打電話? 1/發短信 2/打電話
*/
public class Test1 {
public static void main(String[] args) {
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();
}
}
class Phone{
// synchronized 鎖的對象是方法的調用者!、
// 兩個方法用的是同一個鎖,誰先拿到誰執行!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發短信");
}
public synchronized void call(){
System.out.println("打電話");
}
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 3、 增加了一個普通方法后!先執行發短信還是Hello? 普通方法
* 4、 兩個對象,兩個同步方法, 發短信還是 打電話? // 打電話
*/
public class Test2 {
public static void main(String[] args) {
// 兩個對象,兩個調用者,兩把鎖!
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
//鎖的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕獲
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}}
class Phone2{
// synchronized 鎖的對象是方法的調用者!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發短信");
}
public synchronized void call(){
System.out.println("打電話");
}
// 這里沒有鎖!不是同步方法,不受鎖的影響
public void hello(){
System.out.println("hello");
}
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 5、增加兩個靜態的同步方法,只有一個對象,先打印 發短信?打電話?
* 6、兩個對象!增加兩個靜態的同步方法, 先打印 發短信?打電話?
*/
public class Test3 {
public static void main(String[] args) {
// 兩個對象的Class類模板只有一個,static,鎖的是Class
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
//鎖的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕獲
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
// Phone3唯一的一個 Class 對象class Phone3{
// synchronized 鎖的對象是方法的調用者!
// static 靜態方法
// 類一加載就有了!鎖的是Class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發短信");
}
public static synchronized void call(){
System.out.println("打電話");
}
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 1、1個靜態的同步方法,1個普通的同步方法 ,一個對象,先打印 發短信?打電話?
* 2、1個靜態的同步方法,1個普通的同步方法 ,兩個對象,先打印 發短信?打電話?
*/
public class Test4 {
public static void main(String[] args) {
// 兩個對象的Class類模板只有一個,static,鎖的是Class
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
//鎖的存在
new Thread(()->{
phone1.sendSms();
},"A").start();
// 捕獲
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone2.call();
},"B").start();
}
}
// Phone3唯一的一個 Class 對象
class Phone4{
// 靜態的同步方法 鎖的是 Class 類模板
public static synchronized void sendSms(){
try {小結
new this 具體的一個手機
static Class 唯一的一個模板
6、集合類不安全
List 不安全
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發短信");
}
// 普通的同步方法 鎖的調用者
public synchronized void call(){
System.out.println("打電話");
}
}

 

線程池:三大方法

 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// Executors 工具類、3大方法
public class Demo01 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();// 單個線
// ExecutorService threadPool = Executors.newFixedThreadPool(5); // 創建一
個固定的線程池的大小
// ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸縮
的,遇強則強,遇弱則弱
try {
for (int i = 0; i < 100; i++) {
// 使用了線程池之后,使用線程池來創建線程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 線程池用完,程序結束,關閉線程池
threadPool.shutdown();
}
}
}

 

7大參數

源碼分析
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(5, 5,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 本質ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize, // 核心線程池大小
int maximumPoolSize, // 最大核心線程池大小
long keepAliveTime, // 超時了沒有人調用就會釋放
TimeUnit unit, // 超時單位
BlockingQueue<Runnable> workQueue, // 阻塞隊列
ThreadFactory threadFactory, // 線程工廠:創建線程的,一般
不用動
RejectedExecutionHandler handle // 拒絕策略) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

 

手動創建一個線程池

 

Stream流式計算

什么是Stream流式計算
大數據:存儲 + 計算
集合、MySQL 本質就是存儲東西的;
計算都應該交給流來操作!
package com.kuang.function;
import java.util.function.Supplier;
/**
* Supplier 供給型接口 沒有參數,只有返回值
*/
public class Demo04 {
public static void main(String[] args) {
// Supplier supplier = new Supplier<Integer>() {
// @Override
// public Integer get() {
// System.out.println("get()");
// return 1024;
// }
// };
Supplier supplier = ()->{ return 1024; };
System.out.println(supplier.get());
}
}

ForkJoin

什么是 ForkJoin
ForkJoin 在 JDK 1.7 , 並行執行任務!提高效率。大數據量!
大數據:Map Reduce (把大任務拆分為小任務)
 
import java.util.Arrays;
import java.util.List;
/**
* 題目要求:一分鍾內完成此題,只能用一行代碼實現!
* 現在有5個用戶!篩選:
* 1、ID 必須是偶數
* 2、年齡必須大於23歲
* 3、用戶名轉為大寫字母
* 4、用戶名字母倒着排序
* 5、只輸出一個用戶!
*/
public class Test {
public static void main(String[] args) {
User u1 = new User(1,"a",21);
User u2 = new User(2,"b",22);
User u3 = new User(3,"c",23);
User u4 = new User(4,"d",24);
User u5 = new User(6,"e",25);
// 集合就是存儲
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
// 計算交給Stream流
// lambda表達式、鏈式編程、函數式接口、Stream流式計算
list.stream()
.filter(u->{return u.getId()%2==0;})
.filter(u->{return u.getAge()>23;})
.map(u->{return u.getName().toUpperCase();})
.sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
.limit(1)
.forEach(System.out::println);
}
}ForkJoin 特點:工作竊取
這個里面維護的都是雙端隊列ForkJoin.
package com.kuang.forkjoin;
import java.util.concurrent.RecursiveTask;
/**
* 求和計算的任務!
* 3000 6000(ForkJoin) 9000(Stream並行流)
* // 如何使用 forkjoin
* // 1、forkjoinPool 通過它來執行
* // 2、計算任務 forkjoinPool.execute(ForkJoinTask task)
* // 3. 計算類要繼承 ForkJoinTask
*/
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start; // 1
private Long end; // 1990900000
// 臨界值
private Long temp = 10000L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
// 計算方法
@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else { // forkjoin 遞歸
long middle = (start + end) / 2; // 中間值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
task1.fork(); // 拆分任務,把任務壓入線程隊列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
task2.fork(); // 拆分任務,把任務壓入線程隊列return task1.join() + task2.join();
}
}
}
測試:
package com.kuang.forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
/**
* 同一個任務,別人效率高你幾十倍!
*/
public class Test {
public static void main(String[] args) throws ExecutionException,
InterruptedException {
// test1(); // 12224
// test2(); // 10038
// test3(); // 153
}
// 普通程序員
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();
for (Long i = 1L; i <= 10_0000_0000; i++) {
sum += i;
}
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 時間:"+(end-start));
}
// 會使用ForkJoin
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任務
Long sum = submit.get();
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 時間:"+(end-start));
}
public static void test3(){
long start = System.currentTimeMillis();
// Stream並行流 () (]
long sum = LongStream.rangeClosed(0L,
10_0000_0000L).parallel().reduce(0, Long::sum);
long end = System.currentTimeMillis();15、異步回調
Future 設計的初衷: 對將來的某個事件的結果進行建模
.
System.out.println("sum="+"時間:"+(end-start));
}
}
package com.kuang.future;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* 異步調用: CompletableFuture
* // 異步執行
* // 成功回調
* // 失敗回調
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException,
InterruptedException {
// 沒有返回值的 runAsync 異步回調
// CompletableFuture<Void> completableFuture =
CompletableFuture.runAsync(()->{
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
// });
//
// System.out.println("1111");
//
// completableFuture.get(); // 獲取阻塞執行結果
// 有返回值的 supplyAsync 異步回調
// ajax,成功和失敗的回調16、JMM
 

請你談談你對 Volatile 的理解

Volatile 是 Java 虛擬機提供輕量級的同步機制
1、保證可見性
2、不保證原子性
3、禁止指令重排
什么是JMM
JMM : Java內存模型,不存在的東西,概念!約定!
關於JMM的一些同步的約定:
1、線程解鎖前,必須把共享變量立刻刷回主存。
2、線程加鎖前,必須讀取主存中的最新值到工作內存中!
3、加鎖和解鎖是同一把鎖
線程 工作內存 、主內存
// 返回的是錯誤信息;
CompletableFuture<Integer> completableFuture =
CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
int i = 10/0;
return 1024;
});
System.out.println(completableFuture.whenComplete((t, u) -> {
System.out.println("t=>" + t); // 正常的返回結果
System.out.println("u=>" + u); // 錯誤信息:
java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by
zero
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 233; // 可以獲取到錯誤的返回結果
}).get());
/**
* succee Code 200
* error Code 404 500
*/
}
}
 
8種操作:
內存交互操作有8種,虛擬機實現必須保證每一個操作都是原子的,不可在分的(對於double和long類
型的變量來說,load、store、read和write操作在某些平台上允許例外)
lock (鎖定):作用於主內存的變量,把一個變量標識為線程獨占狀態
unlock (解鎖):作用於主內存的變量,它把一個處於鎖定狀態的變量釋放出來,釋放后的變量
才可以被其他線程鎖定
read (讀取):作用於主內存變量,它把一個變量的值從主內存傳輸到線程的工作內存中,以便
隨后的load動作使用
load (載入):作用於工作內存的變量,它把read操作從主存中變量放入工作內存中
use (使用):作用於工作內存中的變量,它把工作內存中的變量傳輸給執行引擎,每當虛擬機
遇到一個需要使用到變量的值,就會使用到這個指令
assign (賦值):作用於工作內存中的變量,它把一個從執行引擎中接受到的值放入工作內存的變
量副本中
store (存儲):作用於主內存中的變量,它把一個從工作內存中一個變量的值傳送到主內存中,
以便后續的write使用write (寫入):作用於主內存中的變量,它把store操作從工作內存中得到的變量的值放入主內
存的變量中
JMM對這八種指令的使用,制定了如下規則:
不允許read和load、store和write操作之一單獨出現。即使用了read必須load,使用了store必須
write
不允許線程丟棄他最近的assign操作,即工作變量的數據改變了之后,必須告知主存
不允許一個線程將沒有assign的數據從工作內存同步回主內存
一個新的變量必須在主內存中誕生,不允許工作內存直接使用一個未被初始化的變量。就是懟變量
實施use、store操作之前,必須經過assign和load操作
一個變量同一時間只有一個線程能對其進行lock。多次lock后,必須執行相同次數的unlock才能解
如果對一個變量進行lock操作,會清空所有工作內存中此變量的值,在執行引擎使用這個變量前,
必須重新load或assign操作初始化變量的值
如果一個變量沒有被lock,就不能對其進行unlock操作。也不能unlock一個被其他線程鎖住的變量
對一個變量進行unlock操作之前,必須把此變量同步回主內存
問題: 程序不知道主內存的值已經被修改過了
 
Volatile
1、保證可見性
package com.kuang.tvolatile;
import java.util.concurrent.TimeUnit;
public class JMMDemo {
// 不加 volatile 程序就會死循環!
// 加 volatile 可以保證可見性
private volatile static int num = 0;public static void main(String[] args) { // main
new Thread(()->{ // 線程 1 對主內存的變化不知道的
while (num==0){
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
num = 1;
System.out.println(num);
}
}
2、不保證原子性
原子性 : 不可分割
線程A在執行任務的時候,不能被打擾的,也不能被分割。要么同時成功,要么同時失敗。
package com.kuang.tvolatile;
// volatile 不保證原子性
public class VDemo02 {
// volatile 不保證原子性
private volatile static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
//理論上num結果應該為 2 萬
for (int i = 1; i <= 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000 ; j++) {
add();
}
}).start();
}
while (Thread.activeCount()>2){ // main gc
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " " + num);
}}
如果不加 lock 和 synchronized ,怎么樣保證原子性
使用原子類,解決 原子性問題.
package com.kuang.tvolatile;
import java.util.concurrent.atomic.AtomicInteger;
// volatile 不保證原子性
public class VDemo02 {
// volatile 不保證原子性
// 原子類的 Integer
private volatile static AtomicInteger num = new AtomicInteger();
public static void add(){線程A
線程B
x=a
y=b
b=1
a=2
 
 

CAS : 比較當前工作內存中的值和主內存中的值,如果這個值是期望的,那么則執行操作!如果不是就

一直循環!
缺點:
1、 循環會耗時
2、一次性只能保證一個共享變量的原子性
3、ABA問題
CAS : ABA 問題(狸貓換太子).
20、原子引用
解決ABA 問題,引入原子引用! 對應的思想:樂觀鎖!
帶版本號 的原子操作!
package com.kuang.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
// CAS compareAndSet : 比較並交換!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果我期望的值達到了,那么就更新,否則,就不更新, CAS 是CPU的並發原語!
// ============== 搗亂的線程 ==================
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
// ============== 期望的線程 ==================
System.out.println(atomicInteger.compareAndSet(2020, 6666));
System.out.println(atomicInteger.get());
}
}
package com.kuang.cas;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class CASDemo {
//AtomicStampedReference 注意,如果泛型是一個包裝類,注意對象的引用問題
// 正常在業務操作,這里面比較的都是一個個對象
static AtomicStampedReference<Integer> atomicStampedReference = new
AtomicStampedReference<>(1,1);
// CAS compareAndSet : 比較並交換!
public static void main(String[] args) {
new Thread(()->{
int stamp = atomicStampedReference.getStamp(); // 獲得版本號
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1);
System.out.println("a2=>"+atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(2, 1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println("a3=>"+atomicStampedReference.getStamp());
},"a").start();
// 樂觀鎖的原理相同!
new Thread(()->{
int stamp = atomicStampedReference.getStamp(); // 獲得版本號
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 6,
stamp, stamp + 1));
System.out.println("b2=>"+atomicStampedReference.getStamp());
},"b").start();注意:
Integer 使用了對象緩存機制,默認范圍是 -128 ~ 127 ,推薦使用靜態工廠方法 valueOf 獲取對象實
例,而不是 new,因為 valueOf 使用緩存,而 new 一定會創建新的對象分配新的內存空間;

各種鎖的理解

1、公平鎖、非公平鎖

公平鎖: 非常公平, 不能夠插隊,必須先來后到!
非公平鎖:非常不公平,可以插隊 (默認都是非公平)

2、可重入鎖

可重入鎖(遞歸鎖)
}
}
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}Synchronized
package com.kuang.lock;
import javax.sound.midi.Soundbank;
// Synchronized
public class Demo01 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName() + "sms");
call(); // 這里也有鎖
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName() + "call");
}
}Lock 版
package com.kuang.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo02 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
Lock lock = new ReentrantLock();
public void sms(){
lock.lock(); // 細節問題:lock.lock(); lock.unlock(); // lock 鎖必須配對,否
則就會死在里面
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "sms");
call(); // 這里也有鎖
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
lock.unlock();
}
}
public void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "call");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

3、自旋鎖

spinlock
.
我們來自定義一個鎖測試
測試
package com.kuang.lock;
import java.util.concurrent.atomic.AtomicReference;
/**
* 自旋鎖
*/
public class SpinlockDemo {
// int 0
// Thread null
AtomicReference<Thread> atomicReference = new AtomicReference<>();
// 加鎖
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "==> mylock");
// 自旋鎖
while (!atomicReference.compareAndSet(null,thread)){
}
}
// 解鎖
// 加鎖
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "==> myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
package com.kuang.lock;

4、死鎖

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TestSpinLock {
public static void main(String[] args) throws InterruptedException {
// ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();
// reentrantLock.unlock();
// 底層使用的自旋鎖CAS
SpinlockDemo lock = new SpinlockDemo();
new Thread(()-> {
lock.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"T1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()-> {
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"T2").start();
}
}死鎖是什么
.
死鎖測試,怎么排除死鎖:
package com.kuang.lock;
import com.sun.org.apache.xpath.internal.SourceTree;
import java.util.concurrent.TimeUnit;
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA, lockB), "T1").start();
new Thread(new MyThread(lockB, lockA), "T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){System.out.println(Thread.currentThread().getName() +
"lock:"+lockA+"=>get"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName() +
"lock:"+lockB+"=>get"+lockA);
}
}
}
}
解決問題
1、使用 jps -l 定位進程號
2、使用 jstack 進程號 找到死鎖問題
面試,工作中! 排查問題:
1、日志 9
2、堆棧 1

本文資料來自Up主,狂神,記錄下來方便復習用


免責聲明!

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



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