1 什么是JUC
java.util 工具包颅筋、包田轧、分類(lèi)
業(yè)務(wù):普通的線(xiàn)程代碼Thread
Runnable
沒(méi)有返回值儒洛、效率相比入 Callable
相對(duì)較低!
2 線(xiàn)程和進(jìn)程
- 進(jìn)程:一個(gè)程序希太,QQ.exe Music.exe 程序的集合克饶;
- 一個(gè)進(jìn)程往往可以包含多個(gè)線(xiàn)程,至少包含一個(gè)誊辉!
- Java默認(rèn)有幾個(gè)線(xiàn)程矾湃? 2 個(gè) mian、GC
- 線(xiàn)程:開(kāi)了一個(gè)進(jìn)程 Typora堕澄,寫(xiě)字邀跃,自動(dòng)保存(線(xiàn)程負(fù)責(zé)的)
- 對(duì)于Java而言:Thread霉咨、Runnable、Callable
- Java 真的可以開(kāi)啟線(xiàn)程嗎拍屑? 開(kāi)不了
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 無(wú)法直接操作硬件
private native void start0();
2.1 并發(fā)僵驰、并行
并發(fā)編程:并發(fā)喷斋、并行
并發(fā)(多線(xiàn)程操作同一個(gè)資源)
- CPU 一核 ,模擬出來(lái)多條線(xiàn)程蒜茴,天下武功星爪,唯快不破,快速交替
并行(多個(gè)人一起行走)
- CPU 多核 粉私,多個(gè)線(xiàn)程可以同時(shí)執(zhí)行顽腾; 線(xiàn)程池
package com.kuang.demo01;
public class Test1 {
public static void main(String[] args) {
// 獲取cpu的核數(shù)
// CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并發(fā)編程的本質(zhì):充分利用CPU的資源
線(xiàn)程有幾個(gè)狀態(tài)?
public enum State {
// 新生
NEW,
// 運(yùn)行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待诺核,死死地等
WAITING,
// 超時(shí)等待
TIMED_WAITING,
// 終止
TERMINATED;
}
wait/sleep 區(qū)別
-
來(lái)自不同的類(lèi)
wait
=>Object
sleep
=>Thread
-
關(guān)于鎖的釋放
wait
會(huì)釋放鎖抄肖,sleep
睡覺(jué)了,抱著鎖睡覺(jué)猪瞬,不會(huì)釋放憎瘸! -
使用的范圍是不同的
wait
必須在同步代碼塊中,sleep
可以在任何地方睡 -
是否需要捕獲異常
wait
不需要捕獲異常
sleep
必須要捕獲異常
3. Lock鎖(重點(diǎn))
傳統(tǒng) Synchronized
package com.kuang.demo01;
// 基本的賣(mài)票例子
import java.time.OffsetDateTime;
/**
* 真正的多線(xiàn)程開(kāi)發(fā)陈瘦,公司中的開(kāi)發(fā)幌甘,降低耦合性
* 線(xiàn)程就是一個(gè)單獨(dú)的資源類(lèi),沒(méi)有任何附屬的操作痊项!
* 1锅风、 屬性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并發(fā):多線(xiàn)程操作同一個(gè)資源類(lèi), 把資源類(lèi)丟入線(xiàn)程
Ticket ticket = new Ticket();
// @FunctionalInterface 函數(shù)式接口鞍泉,jdk1.8 lambda表達(dá)式 (參數(shù))->{ 代碼 }
new Thread(()->{
for (int i = 1; i < 40 ; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 1; i < 40 ; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 1; i < 40 ; i++) {
ticket.sale();
}
},"C").start();
}
}
// 資源類(lèi) OOP
class Ticket {
// 屬性皱埠、方法
private int number = 30;
// 賣(mài)票的方式
// synchronized 本質(zhì): 隊(duì)列,鎖
public synchronized void sale(){
if (number>0){
System.out.println(Thread.currentThread().getName()+"賣(mài)出了"+(number-
-)+"票,剩余:"+number);
}
}
}
Lock 接口
加鎖咖驮,解鎖操作
實(shí)現(xiàn)類(lèi):
可以選擇是否開(kāi)啟公平鎖
公平鎖:十分公平:可以先來(lái)后到
非公平鎖:十分不公平:可以插隊(duì) (默認(rèn))
- new ReentrantLock();
- lock.lock(); // 加鎖
- finally=> lock.unlock(); // 解鎖
package com.kuang.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketDemo02 {
public static void main(String[] args) {
// 并發(fā):多線(xiàn)程操作同一個(gè)資源類(lèi), 把資源類(lèi)丟入線(xiàn)程
Ticket2 ticket = new Ticket2();
// @FunctionalInterface 函數(shù)式接口边器,jdk1.8 lambda表達(dá)式 (參數(shù))->{ 代碼 }
new Thread(()->{for (int i = 1; i < 40 ; i++)
ticket.sale();},"A").start();
new Thread(()->{for (int i = 1; i < 40 ; i++)
ticket.sale();},"B").start();
new Thread(()->{for (int i = 1; i < 40 ; i++)
ticket.sale();},"C").start();
}
}
// 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 {
// 業(yè)務(wù)代碼
if (number>0){
System.out.println(Thread.currentThread().getName()+"賣(mài)出了"+
(number--)+"票,剩余:"+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 解鎖
}
}
}
Synchronized 和 Lock 區(qū)別
-
Synchronized
內(nèi)置的Java關(guān)鍵字睦刃,Lock
是一個(gè)Java類(lèi) -
Synchronized
無(wú)法判斷獲取鎖的狀態(tài)砚嘴,Lock
可以判斷是否獲取到了鎖 -
Synchronized
會(huì)自動(dòng)釋放鎖,lock
必須要手動(dòng)釋放鎖!如果不釋放鎖际长,死鎖 -
Synchronized
線(xiàn)程 1(獲得鎖耸采,阻塞)、線(xiàn)程2(等待工育,傻傻的等)虾宇;Lock
鎖就不一定會(huì)等待下去; -
Synchronized
可重入鎖翅娶,不可以中斷的文留,非公平;Lock
竭沫,可重入鎖,可以 判斷鎖骑篙,非公平(可以
自己設(shè)置)蜕提; -
Synchronized
適合鎖少量的代碼同步問(wèn)題,Lock
適合鎖大量的同步代碼靶端!
4. 生產(chǎn)者和消費(fèi)者問(wèn)題
生產(chǎn)者和消費(fèi)者問(wèn)題 Synchronized 版
package com.kuang.pc;
/**
* 線(xiàn)程之間的通信問(wèn)題:生產(chǎn)者和消費(fèi)者問(wèn)題谎势! 等待喚醒,通知喚醒
* 線(xiàn)程交替執(zhí)行 A B 操作同一個(gè)變量 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();
}
}
// 判斷等待杨名,業(yè)務(wù)脏榆,通知
class Data{ // 數(shù)字 資源類(lèi)
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);
// 通知其他線(xiàn)程,我+1完畢了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number==0){ // 1
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線(xiàn)程台谍,我-1完畢了
this.notifyAll();
}
}
問(wèn)題存在须喂,A B C D 4 個(gè)線(xiàn)程! 虛假喚醒
原因:if是一次判斷
if 改為 while 判斷
/**
* 線(xiàn)程之間的通信問(wèn)題:生產(chǎn)者和消費(fèi)者問(wèn)題趁蕊! 等待喚醒坞生,通知喚醒
* 線(xiàn)程交替執(zhí)行 A B 操作同一個(gè)變量 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();
}
}
// 判斷等待,業(yè)務(wù)掷伙,通知
class Data{ // 數(shù)字 資源類(lèi)
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);
// 通知其他線(xiàn)程是己,我+1完畢了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number==0){ // 1
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線(xiàn)程,我-1完畢了
this.notifyAll();
}
}
JUC版的生產(chǎn)者和消費(fèi)者問(wèn)題
通過(guò)Lock
找到 Condition
代碼實(shí)現(xiàn):
package com.kuang.pc;
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();
}
}
// 判斷等待任柜,業(yè)務(wù)卒废,通知
class Data2{ // 數(shù)字 資源類(lèi)
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await(); // 等待
//condition.signalAll(); // 喚醒全部
//+1
public void increment() throws InterruptedException {
任何一個(gè)新的技術(shù),絕對(duì)不是僅僅只是覆蓋了原來(lái)的技術(shù)宙地,優(yōu)勢(shì)和補(bǔ)充摔认!
Condition 精準(zhǔn)的通知和喚醒線(xiàn)程
lock.lock();
try {
// 業(yè)務(wù)代碼
while (number!=0){ //0
// 等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他線(xiàn)程,我+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);
// 通知其他線(xiàn)程绸栅,我-1完畢了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
任何一個(gè)新的技術(shù)级野,絕對(duì)不是僅僅只是覆蓋了原來(lái)的技術(shù),優(yōu)勢(shì)和補(bǔ)充!
Condition 精準(zhǔn)的通知和喚醒線(xiàn)程
代碼測(cè)試:
package com.kuang.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author 狂神說(shuō)Java 24736743@qq.com
* A 執(zhí)行完調(diào)用B蓖柔,B執(zhí)行完調(diào)用C辰企,C執(zhí)行完調(diào)用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{ // 資源類(lèi) 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 {
// 業(yè)務(wù),判斷-> 執(zhí)行-> 通知
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 {
// 業(yè)務(wù),判斷-> 執(zhí)行-> 通知
while (number!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
// 喚醒镐捧,喚醒指定的人潜索,c
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
// 業(yè)務(wù),判斷-> 執(zhí)行-> 通知
// 業(yè)務(wù)懂酱,判斷-> 執(zhí)行-> 通知
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();
}
}
}
5. 8鎖現(xiàn)象
如何判斷鎖的是誰(shuí)列牺!永遠(yuǎn)的知道什么鎖整陌,鎖到底鎖的是誰(shuí)!
8個(gè)問(wèn)題深刻理解我們的鎖
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 8鎖瞎领,就是關(guān)于鎖的8個(gè)問(wèn)題
* 1泌辫、標(biāo)準(zhǔn)情況下,兩個(gè)線(xiàn)程先打印 發(fā)短信還是 打電話(huà)九默? 1/發(fā)短信 2/打電話(huà):1
* 1震放、sendSms延遲4秒,兩個(gè)線(xiàn)程先打印 發(fā)短信還是 打電話(huà)驼修? 1/發(fā)短信 2/打電話(huà):1
*/
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 鎖的對(duì)象是方法的調(diào)用者殿遂!、
// 兩個(gè)方法用的是同一個(gè)鎖邪锌,誰(shuí)先拿到誰(shuí)執(zhí)行勉躺!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發(fā)短信");
}
public synchronized void call(){
System.out.println("打電話(huà)");
}
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 3、 增加了一個(gè)普通方法后觅丰!先執(zhí)行發(fā)短信還是Hello饵溅? 普通方法
* 4、 兩個(gè)對(duì)象妇萄,兩個(gè)同步方法蜕企, 發(fā)短信還是 打電話(huà)? // 打電話(huà)
*/
public class Test2 {
public static void main(String[] args) {
// 兩個(gè)對(duì)象冠句,兩個(gè)調(diào)用者轻掩,兩把鎖!
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 鎖的對(duì)象是方法的調(diào)用者懦底!
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發(fā)短信");
}
public synchronized void call(){
System.out.println("打電話(huà)");
}
// 這里沒(méi)有鎖唇牧!不是同步方法,不受鎖的影響
public void hello(){
System.out.println("hello");
}
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 5、增加兩個(gè)靜態(tài)的同步方法丐重,只有一個(gè)對(duì)象腔召,先打印 發(fā)短信?打電話(huà)扮惦?:發(fā)短信
* 6臀蛛、兩個(gè)對(duì)象!增加兩個(gè)靜態(tài)的同步方法崖蜜, 先打印 發(fā)短信浊仆?打電話(huà)?:發(fā)短信
*/
public class Test3 {
public static void main(String[] args) {
// 兩個(gè)對(duì)象的Class類(lèi)模板只有一個(gè)豫领,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唯一的一個(gè) Class 對(duì)象
class Phone3{
// synchronized 鎖的對(duì)象是方法的調(diào)用者!
// static 靜態(tài)方法
// 類(lèi)一加載就有了氏堤!鎖的是Class
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發(fā)短信");
}
public static synchronized void call(){
System.out.println("打電話(huà)");
}
}
package com.kuang.lock8;
import java.util.concurrent.TimeUnit;
/**
* 1沙绝、1個(gè)靜態(tài)的同步方法,1個(gè)普通的同步方法 鼠锈,一個(gè)對(duì)象,先打印 發(fā)短信星著?打電話(huà)购笆?:打電話(huà)
* 2、1個(gè)靜態(tài)的同步方法虚循,1個(gè)普通的同步方法 同欠,兩個(gè)對(duì)象,先打印 發(fā)短信横缔?打電話(huà)铺遂?:打電話(huà)
*/
public class Test4 {
public static void main(String[] args) {
// 兩個(gè)對(duì)象的Class類(lèi)模板只有一個(gè),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唯一的一個(gè) Class 對(duì)象
class Phone4{
// 靜態(tài)的同步方法 鎖的是 Class 類(lèi)模板
public static synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("發(fā)短信");
}
// 普通的同步方法 鎖的調(diào)用者
public synchronized void call(){
System.out.println("打電話(huà)");
}
}
6. 集合類(lèi)不安全
List 不安全
java.util.ConcurrentModificationException
并發(fā)修改異常襟锐!
解決方案:
List<String> list = new Vector<>();
List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArrayList<>();
package com.kuang.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
// java.util.ConcurrentModificationException 并發(fā)修改異常膛锭!
public class ListTest {
public static void main(String[] args) {
// 并發(fā)下 ArrayList 不安全的嗎粮坞,Synchronized;
/**
* 解決方案初狰;
* 1莫杈、List<String> list = new Vector<>();
* 2、List<String> list = Collections.synchronizedList(new ArrayList<>
());
* 3奢入、List<String> list = new CopyOnWriteArrayList<>()筝闹;
*/
// CopyOnWrite 寫(xiě)入時(shí)復(fù)制 COW 計(jì)算機(jī)程序設(shè)計(jì)領(lǐng)域的一種優(yōu)化策略;
// 多個(gè)線(xiàn)程調(diào)用的時(shí)候,list关顷,讀取的時(shí)候糊秆,固定的,寫(xiě)入(覆蓋)
// 在寫(xiě)入的時(shí)候避免覆蓋解寝,造成數(shù)據(jù)問(wèn)題扩然!
// 讀寫(xiě)分離
// CopyOnWriteArrayList 比 Vector Nb 在哪里?
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
Set 不安全
package com.kuang.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 同理可證 : ConcurrentModificationException
* //1聋伦、Set<String> set = Collections.synchronizedSet(new HashSet<>());
* //2夫偶、
*/
public class SetTest {
public static void main(String[] args) {
// Set<String> set = new HashSet<>();
// Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <=30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
hashSet 底層是什么?
public HashSet() {
map = new HashMap<>();
}
// add set 本質(zhì)就是 map key是無(wú)法重復(fù)的觉增!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object(); // 不變得值兵拢!
Map 不安全
回顧Map基本操作
package com.kuang.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
// ConcurrentModificationException
public class MapTest {
public static void main(String[] args) {
// map 是這樣用的嗎? 不是逾礁,工作中不用 HashMap
// 默認(rèn)等價(jià)于什么说铃? new HashMap<>(16,0.75);
// Map<String, String> map = new HashMap<>();
// 唯一的一個(gè)家庭作業(yè):研究ConcurrentHashMap的原理
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <=30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(
0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
7. Callable ( 簡(jiǎn)單 )
- 可以有返回值
- 可以?huà)伋霎惓?/li>
- 方法不同,run()/ call()
代碼測(cè)試
Runnable
實(shí)現(xiàn)類(lèi)
package com.kuang.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.ReentrantLock;
/**
* 1嘹履、探究原理
* 2腻扇、覺(jué)自己會(huì)用
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException,
InterruptedException {
// new Thread(new Runnable()).start();
// new Thread(new FutureTask<V>()).start();
// new Thread(new FutureTask<V>( Callable )).start();
new Thread().start(); // 怎么啟動(dòng)Callable
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread); // 適配類(lèi)
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); // 結(jié)果會(huì)被緩存,效率高
Integer o = (Integer) futureTask.get(); //這個(gè)get 方法可能會(huì)產(chǎn)生阻塞砾嫉!把他放到
最后
// 或者使用異步通信來(lái)處理幼苛!
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() {
System.out.println("call()"); // 會(huì)打印幾個(gè)call
// 耗時(shí)的操作
return 1024;
}
}
細(xì)節(jié):
- 有緩存
- 結(jié)果可能需要等待,會(huì)阻塞焕刮!
8. 常用的輔助類(lèi)(必會(huì))
8.1 CountDownLatch
package com.kuang.add;
import java.util.concurrent.CountDownLatch;
// 計(jì)數(shù)器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 總數(shù)是6舶沿,必須要執(zhí)行任務(wù)的時(shí)候,再使用配并!
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" Go out");
countDownLatch.countDown(); // 數(shù)量-1
},String.valueOf(i)).start();
}
countDownLatch.await(); // 等待計(jì)數(shù)器歸零括荡,然后再向下執(zhí)行
System.out.println("Close Door");
}
}
原理:
-
countDownLatch.countDown();
// 數(shù)量-1 -
countDownLatch.await();
// 等待計(jì)數(shù)器歸零,然后再向下執(zhí)行 - 每次有線(xiàn)程調(diào)用
countDown()
數(shù)量-1溉旋,假設(shè)計(jì)數(shù)器變?yōu)?畸冲,countDownLatch.await()
就會(huì)被喚醒,繼續(xù)
執(zhí)行低滩!
8.2 CyclicBarrier
加法計(jì)數(shù)器
package com.kuang.add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
/**
* 集齊7顆龍珠召喚神龍
*/
// 召喚龍珠的線(xiàn)程
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召喚神龍成功召夹!");
});
for (int i = 1; i <=7 ; i++) {
final int temp = i;
// lambda能操作到 i 嗎
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"收 集"+temp+"個(gè)龍珠");
try {
cyclicBarrier.await(); // 等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
8.3 Semaphore
Semaphore:信號(hào)量
搶車(chē)位!
6車(chē)---3個(gè)停車(chē)位置
package com.kuang.add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
// 線(xiàn)程數(shù)量:停車(chē)位! 限流恕沫!
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
// acquire() 得到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"搶到車(chē) 位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"離開(kāi)車(chē) 位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // release() 釋放
}
},String.valueOf(i)).start();
}
}
}
原理:
-
semaphore.acquire()
獲得监憎,假設(shè)如果已經(jīng)滿(mǎn)了,等待婶溯,等待被釋放為止鲸阔! -
semaphore.release();
釋放偷霉,會(huì)將當(dāng)前的信號(hào)量釋放 + 1,然后喚醒等待的線(xiàn)程褐筛! - 作用: 多個(gè)共享資源互斥的使用类少!并發(fā)限流,控制最大的線(xiàn)程數(shù)渔扎!