創建線程的三種方式
1:繼承thread類,重寫run方法
2:實現runnable接口
3:實現callable接口
第一種方式:繼承thread類
package com.wangjunji.qifeng;
//繼承thread類
public class TestCreateThread extends Thread {
public TestCreateThread(String name) {
super(name);
}
//覆蓋run方法
@Override
public void run() {
for (int i = 0; i < 50; i++) {
//如果繼承thread的類s可以使用this.getId 或 this.getName
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//創建對象
TestCreateThread myThreadName = new TestCreateThread("myThreadName");
TestCreateThread myThreadName1 = new TestCreateThread("myThreadName1");
//調用run方法
myThreadName.setName("我的子線程1");
myThreadName1.setName("我的子線程2");
myThreadName.start();
myThreadName1.start();
myThreadName.join();
myThreadName1.join();
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getId()+":"+i);
}
}
}
獲取和修改線程的名稱
獲取線程ID和線程名稱
1、在thread的子類中調用this.getid() 或this.getName()
2、使用thread.currentThread().getId()和thread.currentThread.getName()
修改線程名稱
1、調用線程對象的setName方法
2、使用線程子類的構造方法賦值 看如上方法
案例:使用繼承thread類實現4個窗口賣100張票
package com.wangjunji.qifeng.second;
public class TiceWIn extends Thread{
private int count =100;
@Override
public void run() {
int num = count;
for (int i = 0; i < num; i++) {
count = count -1;
System.out.println("Thread name:"+this.getName()+":Thread id is :"+this.getId()+":"+count);
}
}
}
package com.wangjunji.qifeng.second;
import com.wangjunji.qifeng.Ticks;
public class Sellor {
public static void main(String[] args) {
TiceWIn t1 = new TiceWIn();
TiceWIn t2 = new TiceWIn();
TiceWIn t3 = new TiceWIn();
TiceWIn t4 = new TiceWIn();
t1.setName("一號窗口");
t2.setName("二號窗口");
t3.setName("三號窗口");
t4.setName("四號窗口");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
四個人賣1000張票
package com.wangjunji.qifeng;
public class SellTickDoor {
public static void main(String[] args) throws InterruptedException {
Ticks ticks = new Ticks();
SellTicks t1 = new SellTicks(ticks);
SellTicks t2 = new SellTicks(ticks);
SellTicks t3 = new SellTicks(ticks);
SellTicks t4 = new SellTicks(ticks);
t1.setName("第一名");
t2.setName("第二名");
t3.setName("第三名");
t4.setName("第四名");
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
}
}
package com.wangjunji.qifeng;
public class SellTicks extends Thread {
private Ticks ticks;
Object obj = new Object();
public SellTicks(Ticks ticks) {
this.ticks = ticks;
}
@Override
public void run() {
while (true) {
if(ticks.getCount()>0) {
synchronized (obj) {
ticks.sellcount();
System.out.println(this.getName() + "賣了一張" + "餘票還有" + ticks.getCount() + "張");
}
}else{
break;
}
}
}
}
package com.wangjunji.qifeng;
public class Ticks {
private int count =1000;
public int getCount() {
return count;
}
public void sellcount(){
count = count -1 ;
}
}
package com.wangjunji.qifeng;
public class MyRunable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
}
}
}
package com.wangjunji.qifeng;
public class Test {
public static void main(String[] args) {
/* MyRunable myRunable = new MyRunable();
new Thread(myRunable).start();*/
Runnable runnable = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("名字" + Thread.currentThread().getName() + "Threadid" + Thread.currentThread().getId() + "i" + i);
}
}
};
new Thread(runnable).start();
for (int i = 0; i < 100; i++) {
System.out.println("名字"+Thread.currentThread().getName()+"Threadid"+Thread.currentThread().getId()+"i"+i);
}
}
}
你和你女朋友共用一個銀行卡,你向卡中存錢,你女朋友從卡中取錢,使用程序模擬過程
package com.wangjunji.qifeng.card;
public class BankCard {
private double money;
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
package com.wangjunji.qifeng.card;
public class AddMony implements Runnable{
BankCard bankCard;
public AddMony(BankCard bankCard) {
this.bankCard = bankCard;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
double money = bankCard.getMoney();
money = money+1000;
bankCard.setMoney(money);
System.out.println(Thread.currentThread().getName()+"存入一千元"+bankCard.getMoney());
}
}
}
package com.wangjunji.qifeng.card;
public class SubMony implements Runnable{
BankCard bankCard;
public SubMony(BankCard bankCard) {
this.bankCard = bankCard;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
double money = bankCard.getMoney();
if(money<0){
System.out.println("餘額不足");
}else{
money = money-1000;
bankCard.setMoney(money);
System.out.println(Thread.currentThread().getName()+"取出一千元"+bankCard.getMoney());
}
}
}
}
package com.wangjunji.qifeng.card;
public class TestBankCard {
public static void main(String[] args) {
BankCard bankCard = new BankCard();
AddMony ad = new AddMony(bankCard);
SubMony sd = new SubMony(bankCard);
Thread chen = new Thread(ad,"晨晨");
Thread bing = new Thread(sd,"冰冰");
chen.start();
bing.start();
}
}
線程的基本狀態
線程對象被創建,即爲初始狀態 new 初始狀態 調用start後進入ready就緒狀態 os選中/時間片到期 running運行狀態
調用start()之後,進入就緒狀態,等待os選中,並分配時間片。
常見方法
休眠 public static void sleep
設置線程優先級
優先級:
線程對象.setPriority()
線程優先級1-10,默認爲5 ,優先級越高,表示獲取cpu機會越多
守護線程:
線程對象.setDaemon(true);設置守護線程
線程有兩類:用戶線程(前強線程),守護線程(後臺線程)
如果程序中所有前臺線程都執行完畢,後臺線程會自動結束
垃圾回收器線程屬於守護線程
package com.wangjunji.qifeng.card;
public class PriorityThread extends Thread {
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
public static void main(String[] args) {
PriorityThread priorityThread = new PriorityThread();
PriorityThread priorityThread1 = new PriorityThread();
priorityThread.setName("thread-1");
priorityThread1.setName("thread-2");
priorityThread.setPriority(10);
priorityThread1.setPriority(1);
priorityThread.start();
priorityThread1.start();
}
}
線程安全問題:
A線程---查找下標0,時間片到期 --->將hello存入下標爲0的位置
B線程---查找下標0,時間片到期--->將world存入下標的位置
需求:A線程將hello存入數組
B線程將world存入數組
多線程安全問題:
當多線程併發訪問臨界資源時,如果破壞原子操作,可能會造成數據不一致。
臨界資源:共享資源(同一對象),一次僅允許一個線絡使用,纔可能保證其正確性。
原子操作,不可分割的多步操作,被視作一個整體,其順序和步驟不可打亂或缺省。
package com.wangjunji.qifeng;
public class ThreadSafe {
private static int index=0;
public static void main(String[] args) throws InterruptedException {
String[] str = new String[5];
Runnable runnable1 = new Runnable() {
@Override
public void run() {
str[index] = "hello";
index++;
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
str[index] = "world";
index++;
}
};
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable1);
Thread thread3= new Thread(runnable);
Thread thread4 = new Thread(runnable1);
thread.start();
thread1.start();
thread3.start();
thread4.start();
thread.join();
thread1.join();
thread3.join();
thread4.join();
for (String s : str) {
System.out.println(s);
}
}
}
思考:在程序中應用中,如何保證線程的安全性。
同步方式(1):
同步代碼塊:synchronized(臨界資源對象) //對臨界資源對象加鎖
//代碼(原子操作)
package com.wangjunji.qifeng;
public class ThreadSafe {
private static int index=0;
public static void main(String[] args) throws InterruptedException {
String[] str = new String[5];
Runnable runnable1 = new Runnable() {
@Override
public void run() {
synchronized (str){
str[index] = "hello";
index++;}
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (str){
str[index] = "world";
index++;}
}
};
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable1);
Thread thread3= new Thread(runnable);
Thread thread4 = new Thread(runnable1);
thread.start();
thread1.start();
thread3.start();
thread4.start();
thread.join();
thread1.join();
thread3.join();
thread4.join();
for (String s : str) {
System.out.println(s);
}
}
}
每個對象都有一個互斥鎖標記,用來分配給線程
只有擁有對象互斥鎖標記的線程,才能進入對該對象加鎖的同步代碼塊。
同步方法:
synchronized返回值類 方法名稱(形參列表0) //對當前對象this加鎖 ,如果是靜態方法則是 class
同步方法:
注:只有擁有對象的互斥標記的線程,才能進入該對象加鎖的同步方法中。
線程退出同步方法時,會釋放相應的互斥鎖標記。
同步規則:
注意:只有在調用包含同步代碼塊的方法,或者同步方法時,才需要對象的鎖標記。
如調用不包含不包含代碼的方法,或普通方法時,則不需要鎖標記,可直接調用。
已知jdk中線程安全的類
stringbuffer
vector
hashtable
以上類中的公開方法,均爲synchnoized的同步方法。
經典問題:
死鎖:
當第一個線程擁有a對象鎖標記,並等待b標記,同時第二個線程擁有b對象鎖標記,並等待A對象鎖標記時,產生死鎖。
一個線程可以同時擁有多個對象的鎖標記,當線程阻塞時,不會釋放已經擁有的鎖標記,因此可能造成死鎖。
//死鎖代碼
package com.wangjunji.qifeng.card;
public class Resrouce {
public static Object objA = new Object();
public static Object objB = new Object();
}
//
package com.wangjunji.qifeng.card;
public class BoyInfo extends Thread {
public void run(){
synchronized (Resrouce.objA){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("boygetInfo get a");
synchronized (Resrouce.objB){
System.out.println("boygetinfo get b ");
}
}
}
}
//
package com.wangjunji.qifeng.card;
public class grirlInfo extends Thread{
public void run(){
synchronized (Resrouce.objB){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("grirlInfo get b");
synchronized (Resrouce.objA){
System.out.println("grirlInfo get a ");
}
}
}
}
package com.wangjunji.qifeng.card;
public class UseInfo {
public static void main(String[] args) {
BoyInfo boyInfo = new BoyInfo();
grirlInfo grilinfo = new grirlInfo();
boyInfo.start();
grilinfo.start();
}
}
線程通信
等待:等待隊列
public final void wait()
public final void wait(long timeout )
必須在對obj加鎖的同步代碼塊中,在一個線程中,調用obj.wait()時,此線程會釋放其擁有所有鎖標記,同時此線程阻塞在o的等待隊列中,釋放鎖,進入等待隊列。
通知:
public final void notify()
public final void notifyall()
經典問題:
生產者,消費者
若干個生產者在生產產品,這些產品將提供給若干個消費者去消費,爲了使用生產者和消費者能併發執行,在兩者之間一個能存儲多個產品的緩衝區,生產者將生產的產品放入緩衝區中,消費者從緩衝區中取走產品進行消費,顯然生產者和消費者之間必須保持同步,即不允許消費者到一個空的緩衝區中取產品,也不允許生產向一個滿的緩衝區中放入產品。
線程的創建:
繼承thread
實現runnable接品,傳入給thread對象執行。
線程安全:
同步方法塊:爲方法中的局部代碼加鎖
同步方法,爲方法的所有代碼加鎖
高級多線程:
1)線程池概念
問題:
線程是寶貴的內存資源,單個線程約佔1Mb空間,過多分配易造成內存溢出。
頻繁的創建及銷燬線程會增加虛擬機回收頻率,資源開銷,造成程序性能下降。
池程池:
線程容器,可設定線程分配的數量的上限。
將預先創建的線程對象存入池中,並重用線程池中的線程對象
避免頻繁的創建和銷燬。
線程池原理:
task1
task2 -----> threadpool
task3
將任務提交給線程池,由線程池分配線程,運行任務,並在當前任務結束後複用線程。
創建線程池:
常用的線程池接口和類(所有包java.util.concurrent裏)
Executor 線程池的頂級接口
executorservice:線程線接口,可通過submit(runable task)提交任務代碼。
executors工廠類,通過此類可以獲得一個線程池。
通過newFixedThreadpool(int nthreads)獲取固定數量的線程池,參數指線程池中線程的數量。
通過newCachedthreadpools獲得動態數量的線程池,如不夠則創建新的,沒有上限。
創建線程池
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 演示線程池的創建
* Executor:線程池的根接口,execute()
* executorService:包含管理線程池的一些方法,submit shutdown
* threadpoolExecutor
* scheduledthreadpoolexecutor
* executor:創建線程池的工具類
* (1) 創建固定線程個數池程池
* (2) 創建緩存線程池,由任務的多少決定
* (3):創建單線程池
* (4)創建調度線路程池,調度,週期,定時執行。
*
*/
public class Demo01 {
public static void main(String[] args) {
//設置提交的個數
//ExecutorService executorService = Executors.newFixedThreadPool(4);
//不設置提交的個數,創緩存線程池,線程個數
ExecutorService executorService = Executors.newCachedThreadPool();
// 創建單線程Executors.newSingleThreadExecutor();
//創建調度線程池,調度,週期,定時執行 Executors.newScheduledThreadPool(5);
Runnable runnable = new Runnable() {
private int ticket = 10000;
public void run() {
while (true){
if(ticket<=0){
break;
}
ticket--;
System.out.println(Thread.currentThread().getName()+"買了第"+ticket+"張票");
}
}
};
//提交任務
for (int i = 0; i < 4; i++) {
executorService.submit(runnable);
}
//關閉線程,不然程序結束不了,等待所有任務執行完畢,才能結束。
executorService.shutdown();
}
}
Callable接口:
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
jdk1.5加入,與runnable接口類似,實現之後代表一個線程任務
callable具有泛型返回值,可以聲明異常。
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 演示callable接口的使用
* callable和runable接口的區別
* callable接口中call方法有返回值,runbable中run方法沒有返回值
* callable接口中call方法有聲明異常,runable接口中run方法沒有異常
*/
public class Demo02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//功能需求,使用callable實現1~100的和
//創建callable對象
Callable<Integer> callable= new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"開始計算");
int sum=0;
for (int i = 1; i <= 100; i++) {
sum=sum+i;
}
return sum;
}
};
//2。不能交給線程。需要把callable對象轉成任務
FutureTask<Integer> task = new FutureTask<>(callable);
//創建線程
Thread thread = new Thread(task);
//啓動線程
thread.start();
//獲取 結果,等等call執行完畢,返回結果
Integer integer = task.get();
System.out.println(integer);
}
}
使用callable與線程池結合起來使用
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.*;
/**
* callable與線程池結合起來用
* 使用線程池來計算1-100的和
*/
public class Demo03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
//提交任務,表示將要執行完任務的結果
Future<Integer> taskinfo = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("開始執行作務");
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum = sum + i;
}
return sum;
}
});
//3獲取結果
System.out.println(taskinfo.get());
executorService.shutdown();
}
}
Future接口 :
future表示將要完成任務的結果
需求,使用兩個線程,併發計算1~50,51~100的和,再進行彙總計算。
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.*;
public class Demo04 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService task = Executors.newFixedThreadPool(2);
Future<Integer> item1 = task.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 50; i++) {
sum = sum + i;
}
return sum;
}
});
Future<Integer> item2 = task.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 50; i <= 100; i++) {
sum = sum + i;
}
return sum;
}
});
int allcount =item1.get()+item2.get();
System.out.println(allcount);
task.shutdown();
}
}
future接口
表示executorservice.sumbit所返回的狀態結果,就是call的返回值
方法:v get()以阻塞形式等待,future中的異步處理結果(call的返回值)
思考什麼是同步,什麼是異步
同步:形容一次方法調用,同步一旦開始,調用者必須等待該方法返回,才能繼續
異步:形容一次調用,異步一旦開始,像是一次消息傳遞,調用者告知之後立刻返回,二者競爭時間片,併發執行。
Lock接口:
jdk5加入,與synchronized比較,顯示定義,結構更靈活。
提供更多實用方法,功能更強大,性能更優越。
常用方法:
void lock()://獲取鎖,如鎖被佔用,則等待
boolean tryLock()://嘗試獲取鎖,成功返回true,失敗返回false,不阻塞
void unlock釋放鎖。
重入鎖:
拿多次鎖,稱爲重入鎖
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo06 implements Runnable {
private int tickCount = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try{
if(tickCount<=0){
break;
}
System.out.println(Thread.currentThread().getName()+":"+tickCount);
tickCount--;
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(4);
Demo06 demo06 = new Demo06();
executorService.submit(demo06);
executorService.submit(demo06);
executorService.submit(demo06);
executorService.submit(demo06);
executorService.shutdown();
}
}
讀寫鎖:
reentantreadwritelock
一種支持一寫多讀的同步鎖,讀寫分離,可分別分配讀鎖,寫鎖
支持多次分配讀鎖,使用多個讀操作可以併發執行。
互斥規則,
寫一寫,互斥,阻塞
讀-寫,互斥,讀阻塞,寫阻塞讀
讀一讀,不互斥,不阻塞
在讀操作遠遠高於寫操作的環境中,可保障線程安全情況下,提高運行效率。
package com.wangjunji.qifeng.threadpool;
import java.security.Security;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
public class Demo07 {
private ReentrantReadWriteLock rr1 = new ReentrantReadWriteLock();
private ReadLock readLock = rr1.readLock();
private WriteLock writeLock = rr1.writeLock();
private String value;
public String getValue(){
readLock.lock();
try{
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("讀取value"+value);
return this.value;
} finally {
readLock.unlock();
}
}
public void setValue(String value){
writeLock.lock();
try{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("寫入value"+value);
this.value = value;
}finally {
writeLock.unlock();
}
this.value = value;
}
public static void main(String[] args) {
Demo07 demo07 = new Demo07();
//創建20個的線程池
ExecutorService executorService = Executors.newFixedThreadPool(40);
for (int i = 0; i < 22; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
demo07.setValue(Thread.currentThread().getName()+new Random().nextInt(100));
}
});
}
for (int i = 0; i < 18; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
demo07.getValue();
}
});
}
executorService.shutdown();
//如果是true,則執行完成
executorService.isTerminated();
}
}
線程安全的集合
collection體系集合中,除vector以外的線程安全集合。
package com.wangjunji.qifeng.threadpool;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class Dem09 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
//使用collections中的線程安全方法轉換線程安全的集合。
Collection<String> strings = Collections.synchronizedCollection(arrayList);
//java併發包裏面的
/*ArrayList<String> arrayList = new ArrayList<>();
Collection<String> strings = Collections.synchronizedCollection(arrayList);*/
CopyOnWriteArrayList<String> strings = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 10; i1++) {
strings.add(Thread.currentThread().getName()+":"+temp+":"+i1);
System.out.println(strings.toString());
}
}
}).start();
}
}
}
CopyOnWriteArrayList:
線程安全的arraylist,加強版的讀寫分離
寫有鎖,讀無鎖,讀寫之間不阻塞,優於讀寫鎖。
寫入時,先copy一個容器副本,再添加新元素,最後替換引用。 以空間來換取安全。
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo10 {
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 100; i1++) {
list.add(i1);
}
}
});
}
//這個結束的時候,有可能所有的子任務還沒有完成,需要增加以下的代碼
executorService.shutdown();
System.out.println(list.size());
while (!executorService.isTerminated()){
}
System.out.println(list.size());
}
}
CopyOnWriteArraySet
線程安全的Set,底層使用copyOnWriteArrayList實現
唯一不同在於,使用addifAbsent添加元素,會遍歷數組
如果存在元素,則不添加(扔掉副本)
Quene接口(隊列)
collection的子接口,表示隊列Fifo()先進先出
棧,先進後出
常用方法:
都需要拋出異常
boolean add(E e):順序添加一個元素(到達上限後,再添加則會拋出異常)
E remove 獲得一個元素並移除(如果隊列沒有元素,則拋出異常)
E element() 獲得一個元素,但不移除,如果隊列沒有元素,則拋出異常。
返回特殊值:推薦使用
boolean offer(E e)//順序添加一個元素(到達上限後,再添加則會返回false)
E poll獲得一個元素並移除 (如果隊列沒有元素時,則返回null)
E peek() 獲得一個元素但不移除 (如果隊列沒有元素時,則返回null)
package com.wangjunji.qifeng.threadpool;
import java.util.LinkedList;
import java.util.Queue;
public class Demo11 {
public static void main(String[] args) {
Queue<String> fruits = new LinkedList<>();
fruits.offer("蘋果");
fruits.offer("葡萄");
fruits.offer("香蕉");
fruits.offer("梨子");
fruits.offer("葡萄");
fruits.offer("蘋果");
fruits.offer("葡萄");
fruits.offer("香蕉");
fruits.offer("梨子");
fruits.offer("葡萄");
System.out.println(fruits.size());
int length = fruits.size();
for (int i = 0; i < length; i++) {
System.out.println(fruits.poll());
}
}
}
ConcurrentLinkedQueue
線程安全、可高效讀寫的隊列,高併發下性能最好的隊列
無鎖,cas 比較交換算法,修改的方法包含三個核心參數(Ven)
v要更新的變量
E預期值
N新值。
只有當v==e時,v=n,否則表示已被更新過,則取消當前操作。
BlockingQueue接口(阻塞隊列)
Queue的子接口,阻塞隊列,增加了兩個線程狀態爲無限期的等待的方法。
方法
void put(E e)將指定的元素插入此隊列中,如果沒有可用空間,則等待
E take 獲得並移除此隊列的頭部元素,如果沒有可用元素,則等待。
可用於解決產生、消費的問題
阻塞隊列
ArrayBlockingQueue:
數組結構實現,有界隊列(手工固定上限)
BlockingQueue<String> abq = new ArrayBlockingQueue<String> (10);
LinkedBlockingQueue
鏈表結構實現,有界隊列(默認上限integer.Max_Value)
BlockingQueue<String> abq = new LinkedBlockingQueue <String> (10);
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
/**
* 阻塞隊列的使用
* 案例1:創建一個有界隊列,添加數據
* 案例2:使用阻塞隊列實現生產者和消費者
*/
public class Demo13 {
public static void main(String[] args) throws Exception {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
queue.put("aaa");
queue.put("aaa");
queue.put("aaa");
queue.put("aaa");
}
}
生產者,消費者
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ArrayBlockingQueue;
public class Demo14 {
public static void main(String[] args) {
ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(6);
Thread zhengzheng = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("生產了第" + i + "個麪包");
queue.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread bingbing = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消費" + queue.take() + "個麪包" );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
zhengzheng.start();
bingbing.start();
}
}
ConcurrentHashMap:
初始容量默認爲16段,使用分段鎖設計
不對整個map加鎖,而是爲每個segment加鎖
當多個對象存入同一個segment時,才需要互斥
最理想的狀態爲16個對象分別存入16個segemnt,並行數量爲16.
使用方式與hashmap無異
package com.wangjunji.qifeng.threadpool;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo15 {
public static void main(String[] args) {
ConcurrentHashMap<String,String> hashMap = new ConcurrentHashMap<String,String>();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
for (int i1 = 0; i1 < 1000; i1++) {
hashMap.put(Thread.currentThread().getName()+":"+i1,"value"+ String.valueOf(Math.random()*1000));
}
}
});
}
executorService.shutdown();
while (!executorService.isTerminated()){
}
System.out.println(hashMap);
}
}