juc
juc
在java中,线程是一个非常重要的部分,JUC就是java.util.concurrent,这是一个处理处理线程的工具包,jdk1.5时出现。
多线程的编程步骤
1、创建资源类,在资源类创建属性和操作方法
2、在资源类操作方法
判断、干活、通知
3、创建多个线程,调用该资源类的操作方法
Lock接口
synchronized关键字
同步方法(普通方法是this,静态方法是类.class)
同步代码块(自定义锁)
public class JUCTest {
public static void main(String[] args){
Thread thread1=new Thread(new Seller(),"一号");
Thread thread2=new Thread(new Seller(),"二号");
Thread thread3=new Thread(new Seller(),"三号");
thread1.start();
thread2.start();
thread3.start();
}
}
class Ticket{
private static int num=30;
public synchronized static void sellTicket(){
if(num>0){
System.out.println(Thread.currentThread().getName()+"第"+Ticket.getNum()+"张票");
num--;
}
}
public static int getNum(){
return num;
}
}
class Seller implements Runnable{
@Override
public void run() {
while(true){
Ticket.sellTicket();
}
}
}
Lock
jdk5.0之后,我们可以通过显示定义同步锁来实现同步,同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具,锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应该获得Lock对象
ReentrantLock类(Lock的实现类 可重入锁)拥有和synchronized相同的并发性和内存语义。
当然我们也要保证这几个线程使用的lock是唯一的(继承的方式创建多线程,将ReentranLock属性设置为静态)。
其实可重入锁相当于一个门,门里面就是我们的资源类,想要进入门的就是我们的多个线程对象。
import java.util.concurrent.locks.ReentrantLock;
public class JUCTest {
public static void main(String[] args){
for(int i=0;i<100;i++){
new Thread(new Seller(),i+"号").start();
}
}
}
class Ticket{
private ReentrantLock reentrantLock=new ReentrantLock();
//可以看看将fair属性设置为false,这个锁会怎么样(观察结果)
private int num=30;
public void sellTicket(){
while(true){
reentrantLock.lock();
try {
if(num<=0){
//即使是break跳出,但是只要他在try代码块中,也会执行finally代码块
break;
}
System.out.println(Thread.currentThread().getName()+"第"+num+"张票");
num--;
}finally {
reentrantLock.unlock();
}
}
}
}
class Seller implements Runnable{
private static Ticket ticket=new Ticket();
@Override
public void run() {
ticket.sellTicket();
}
}
通过Lock可以知道有没有成功获取到锁,synchronized却无法做到
通过Lock提高多个线程进行读操作的效率
在性能上说,如果竞争资源不是很激烈,那么两者的性能是差不多的,但是如果竞争资源十分激烈(大量线程同时竞争),此时Lock的效率是要远远大于synchronized的
线程通信(wait和notify 、notifyAll函数)
再次强调:
wait是等待阻塞,是Object类的一个方法,wait过程中会释放对象锁,只有当其他线程调用notify,notifyAll方法的时候,这个线程才会被再次唤醒,由于wait、notify、notifyAll这三个方法需要获取对象锁,所以应该在synchronized修饰的代码中执行(同步代码块或者同步函数),否则会抛出IllegalMonitorStateException异常。
我们要实现两个线程,一个负责加1,一个负责减1,让这个数始终保持0 或者 1
import java.util.concurrent.locks.ReentrantLock;
public class JUCTest {
public static void main(String[] args){
Num num=new Num();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.add();
}
}
},"线程1 ");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.sub();
}
}
},"线程2 ");
thread1.start();
thread2.start();
}
}
class Num{
private int num=0;
public synchronized void add(){
notify();
System.out.print(Thread.currentThread().getName()+" ");
num++;
show();
try{
wait();
}catch (Exception e){
e.printStackTrace();
}
}
public synchronized void sub(){
notify();
System.out.print(Thread.currentThread().getName()+" ");
num--;
show();
try{
wait();
}catch (Exception e){
e.printStackTrace();
}
}
private void show(){
System.out.println(num);
}
}
如果是四个线程呢(两个加1,两个减1)?非常明显这样通过notify是无法实现的
import java.util.concurrent.locks.ReentrantLock;
public class JUCTest {
public static void main(String[] args){
Num num=new Num();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.add();
}
}
},"线程1 ");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.sub();
}
}
},"线程2 ");
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.add();
}
}
},"线程3 ");
Thread thread4=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.sub();
}
}
},"线程4 ");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Num{
private int num=0;
public synchronized void add(){
try {
if(num==1){
wait();
}
num++;
System.out.print(Thread.currentThread().getName()+" ");
show();
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
public synchronized void sub(){
try {
if(num==0){
wait();
}
System.out.print(Thread.currentThread().getName()+" ");
num--;
show();
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
private void show(){
System.out.println(num);
}
}

此时十分清晰的出现,当测试次数足够多的时候,会出现判断不生效的情况(虚假唤醒)
虚假唤醒其实就是我们编程的错误,产生这种错误的原因就是,我们不知道
wait阻塞被唤醒的时候,从他开始阻塞的地方开始执行。
import java.util.concurrent.locks.ReentrantLock;
public class JUCTest {
public static void main(String[] args){
Num num=new Num();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.add();
}
}
},"线程1 ");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.sub();
}
}
},"线程2 ");
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.add();
}
}
},"线程3 ");
Thread thread4=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.sub();
}
}
},"线程4 ");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Num{
private int num=0;
public synchronized void add(){
try {
while(num==1){
wait();
}
num++;
System.out.print(Thread.currentThread().getName()+" ");
show();
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
public synchronized void sub(){
try {
while(num==0){
wait();
}
System.out.print(Thread.currentThread().getName()+" ");
num--;
show();
notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
private void show(){
System.out.println(num);
}
}
通过lock实现上面代码
Lock接口有一个newCondition()方法,可以返回绑定到此Lock实例的新的Condition实例
Condition接口存在await()、signal()、signalAll()三个方法
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class JUCTest {
public static void main(String[] args){
Num num=new Num();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.add();
}
}
},"线程1 ");
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.sub();
}
}
},"线程2 ");
Thread thread3=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.add();
}
}
},"线程3 ");
Thread thread4=new Thread(new Runnable() {
@Override
public void run() {
while(true){
num.sub();
}
}
},"线程4 ");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
class Num{
private int num=0;
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
public void add(){
lock.lock();
try {
while(num==1){
condition.await();
}
num++;
System.out.print(Thread.currentThread().getName()+" ");
show();
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public synchronized void sub(){
lock.lock();
try {
while(num==0){
condition.await();
}
System.out.print(Thread.currentThread().getName()+" ");
num--;
show();
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
private void show(){
System.out.println(num);
}
}
线程定制化通信
通过设置一个flag变量,创建一个Lock的多个Condition对象来进行控制
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class JUCTest {
public static void main(String[] args){
MyPrintTest myPrintTest=new MyPrintTest();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++)
myPrintTest.print2(i);
}
},"线程1").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++)
myPrintTest.print4(i);
}
},"线程2").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<10;i++)
myPrintTest.print6(i);
}
},"线程3").start();
}
}
class MyPrintTest{
//flag=n执行线程n
private int flag=1;
private ReentrantLock reentrantLock=new ReentrantLock();
private Condition condition1=reentrantLock.newCondition();
private Condition condition2=reentrantLock.newCondition();
private Condition condition3=reentrantLock.newCondition();
public void print2(int loop){
reentrantLock.lock();
try {
while(flag!=1){
condition1.await();
}
for(int i=0;i<2;i++){
System.out.println(i+" "+Thread.currentThread().getName()+" "+loop);
}
flag=2;
condition2.signal();
}
catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
public void print4(int loop){
reentrantLock.lock();
try {
while(flag!=2){
condition2.await();
}
for(int i=0;i<4;i++){
System.out.println(i+" "+Thread.currentThread().getName()+" "+loop);
}
flag=3;
condition3.signal();
}
catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
public void print6(int loop){
reentrantLock.lock();
try {
while(flag!=3){
condition3.await();
}
for(int i=0;i<6;i++){
System.out.println(i+" "+Thread.currentThread().getName()+" "+loop);
}
flag=1;
condition1.signal();
}
catch (Exception e){
e.printStackTrace();
}finally {
reentrantLock.unlock();
}
}
}
JUC辅助类-CountDownLatch
CountDownLatch可以设置一个计数器,然后通过countDown方法来进行减一操作,使用await方法等待计数器不大于0,然后继续执行await方法之后的语句。

import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch=new CountDownLatch(6);
for(int i=0;i<6;i++){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"同学离开教室了");
countDownLatch.countDown();
}
},i+"号").start();
}
//进行堵塞
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"锁门了");
}
}
JUC辅助类-CyclicBarrier
CyclicBarrier的构造方法第一个参数是目标障碍数,每次执行CyclicBarrier后障碍数会加一,如果达到目标障碍数,才会执行await方法。

import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args){
CyclicBarrier cyclicBarrier=new CyclicBarrier(7, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"集齐龙珠召唤神龙");
}
});
for(int i=0;i<7;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"得到龙珠");
cyclicBarrier.await();
}catch (Exception e){
e.printStackTrace();
}
}
},i+1+"号").start();
}
}
}

根据结果我们可以知道,我们达到目标障碍数后,执行的方法并不是新开的一个线程,而是在执行CyclicBarrier的某个线程中执行。
Semaphore

Semaphore构造方法传入的第一个参数是最大信号量(最大线程数),每个信号量初始化为一个最多只能分发一个许可证,使用acquire获取许可证,使用release释放许可证
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args){
Semaphore semaphore=new Semaphore(3);
for(int i=0;i<6;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("我要进去停车"+Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2);
System.out.println("我要出去了"+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}
},i+"号").start();
}
}
}

JUC读写锁(无论是读锁还是写锁都可能会发生死锁)

1线程先操作第一条记录,在进行第二条记录的操作;2线程先操作第二条记录,在进行第一条记录的操。这样是会互相等待的产生死锁。
读锁:共享锁(允许多个线程一起操作),
写锁:独占锁(不允许多个线程一起操作),
读写锁:一个资源可以被多个读线程访问,也可以被一个写线程访问。读和写是不可以同时访问的。
ReentrantReadWriteLock
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWirteLoclDemo {
public static void main(String[] args){
MyCache myCache=new MyCache();
for(int i=0;i<5;i++){
final int num=i;
new Thread(new Runnable() {
@Override
public void run() {
myCache.write(num+"key",num+"value");
}
},i+"号").start();
}
for(int i=0;i<5;i++){
final int num=i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(myCache.read(num+"key"));
}
},i+"号").start();
}
}
}
class MyCache{
private ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock(false);
private volatile Map<String,Object> map=new HashMap<String, Object>();
public void write(String key,Object value){
try {
//reentrantReadWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName()+"正在进行写操作"+key+value);
TimeUnit.SECONDS.sleep(1);//相当于程序执行时间
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"完成写操作"+key+value);
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantReadWriteLock.writeLock().unlock();
}
}
public Object read(String key){
Object result=null;
try {
reentrantReadWriteLock.readLock().lock();
System.out.println(Thread.currentThread().getName()+"正在进行读操作"+key);
TimeUnit.SECONDS.sleep(1);//相当于程序执行时间
result=map.get(key);
System.out.println(Thread.currentThread().getName()+"完成读操作"+key);
}catch (Exception e){
e.printStackTrace();
}finally {
reentrantReadWriteLock.readLock().unlock();
return result;
}
}
}
我们可以观察一下执行结果:
如果我们没有加上读写锁:

加上读写锁:

读写锁降级(JDK8):

public static void main(String[] args){
ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock();
reentrantReadWriteLock.writeLock().lock();
System.out.println("进行写操作");
reentrantReadWriteLock.readLock().lock();
System.out.println("进行读操作");
reentrantReadWriteLock.writeLock().unlock();
reentrantReadWriteLock.readLock().unlock();
}

读锁不能升级为写锁。
public static void main(String[] args){
ReentrantReadWriteLock reentrantReadWriteLock=new ReentrantReadWriteLock();
reentrantReadWriteLock.readLock().lock();
System.out.println("进行读操作");
reentrantReadWriteLock.writeLock().lock();
System.out.println("进行写操作");
reentrantReadWriteLock.readLock().unlock();
reentrantReadWriteLock.writeLock().unlock();
}

然后不会进行写操作的
乐观锁和悲观锁

不支持并发操作

支持并发操作,修改后不仅仅修改操作数据,也要将版本号一起修改,当然在你每次读的时候都要获取版本号,以便于你修改的时候方便进行版本号的比对
表锁和行锁
表锁:操作这张表的时候整个表都上锁,其他线程不可以进行操作。
行锁:操作某一行的时候,只对这一行进行锁定
行锁是会发生死锁的。
线程池
提前创建号多个线程,放入线程池中,使用的时候直接获取,使用完放回线程池中。可以避免频繁创建和销毁线程,实现重复使用。
优势:
1、提高响应速度
2、降低资源损耗
3、便于线程管理
jdk5.0之后提供了线程池支持,并且提供了相关API:ExecutorService和Executors

几种常用的线程池:
1、一池N线程Executors.newFixedThreadPool(int n)
线程池中的线程处于一定的量,可以很好地控制线程的并发量
线程可以被重复使用,在显示关闭之前,都将一直存在
超出一定量的线程被提交的时候需要在队列中进行等待。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args){
ExecutorService threadPool1= Executors.newFixedThreadPool(5);
for(int i=0;i<10;i++){
threadPool1.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"完成任务");
}
});
}
//回收
threadPool1.shutdown();
}
}

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
2、一个任务一个任务的执行,一池一线程Executors.newSingleThreadExecutor()
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args){
ExecutorService threadPool2= Executors.newSingleThreadExecutor();
for(int i=0;i<10;i++){
threadPool2.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"完成任务");
}
});
}
//回收
threadPool2.shutdown();
}
}

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
3、线程池根据需求创建线程,可以扩容Executors.newCachedThreadPool()
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args){
ExecutorService threadPool3= Executors.newCachedThreadPool();
for(int i=0;i<10;i++){
threadPool3.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"完成任务");
}
});
}
//回收
threadPool3.shutdown();
}
}

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ThreadPoolExecutor的七个参数
corePoolSize:线程池中常驻线程数目
maximumPoolSize:线程池中最大支持多少线程数
keepAliveTime:unit:线程存活时间(当线程池中任务骤增,线程池肯定不会是常驻线程数,线程数目肯定上涨,然后此时如果一个线程一定时间没有被使用(被唤醒)就会被消除,从而慢慢恢复到常驻线程数)
workQueue:堵塞队列
threadFactory:线程工厂,用于创建线程
(RejectedExecutionHandler)handler:拒绝策略
工作流程和拒绝策略

注意点:
当我们创建出线程池的时候(Executors.newCachedThreadPool()等等操作的时候)其实没有创建线程,只有当执行execute方法的时候线程才会被创建。
上图中,我们假设 常驻线程数=3 最大线程数=5 阻塞队列长度=3。
1-2由常驻线程执行 3-5进入堵塞队列 6-8由线程池创建新线程执行
9。。。。。。拒绝策略
拒绝策略:

自定义线程池:

实际开发中我们一般是自定义线程池进行使用
import java.util.concurrent.*;
public class ThreadPool {
public static void main(String[] args){
ExecutorService threadPool=new ThreadPoolExecutor(
2,
5,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
for(int i=0;i<10;i++){
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"完成任务");
}
});
}
//回收
threadPool.shutdown();
}
}

执行了拒绝策略
分支合并框架Fork/Join

ForkJoinTask
ForkJoinTask代表运行在ForkJoinPool中的任务。
主要方法:
- fork() 在当前线程运行的线程池中安排一个异步执行。简单的理解就是再创建一个子任务。
- join() 当任务完成的时候返回计算结果。
- invoke() 开始执行任务,如果必要,等待计算完成。
子类:
- RecursiveAction 一个递归无结果的ForkJoinTask(没有返回值)
- RecursiveTask 一个递归有结果的ForkJoinTask(有返回值)
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class ForkJoinDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyTask myTask=new MyTask(1,100);
ForkJoinPool forkJoinPool=new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask=forkJoinPool.submit(myTask);
Integer result=forkJoinTask.get();
System.out.println(result);
}
}
class MyTask extends RecursiveTask<Integer>{
private int begin;
private int end;
private int result;
private static final Integer Value=10;
public MyTask(int begin,int end){
this.begin=begin;
this.end=end;
}
@Override
protected Integer compute() {
if((end-begin)<=Value){
for(int i=begin;i<=end;i++){
result+=i;
}
}else{
int middle=(begin+end)/2;
MyTask myTask1=new MyTask(begin,middle);
MyTask myTask2=new MyTask(middle+1,end);
myTask1.fork();
myTask2.fork();
result=myTask1.join()+myTask2.join();
}
return result;
}
}

ForkJoinPool
ForkJoinPool构造参数:
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode)
-
parallelism:可并行级别,Fork/Join框架将依据这个并行级别的设定,决定框架内并行执行的线程数量。并行的每一个任务都会有一个线程进行处理,但是千万不要将这个属性理解成Fork/Join框架中最多存在的线程数量,也不要将这个属性和ThreadPoolExecutor线程池中的corePoolSize、maximumPoolSize属性进行比较,因为ForkJoinPool的组织结构和工作方式与后者完全不一样。而后续的讨论中,读者还可以发现Fork/Join框架中可存在的线程数量和这个参数值的关系并不是绝对的关联(有依据但并不全由它决定)。
-
factory:当Fork/Join框架创建一个新的线程时,同样会用到线程创建工厂。只不过这个线程工厂不再需要实现ThreadFactory接口,而是需要实现ForkJoinWorkerThreadFactory接口。后者是一个函数式接口,只需要实现一个名叫newThread的方法。在Fork/Join框架中有一个默认的ForkJoinWorkerThreadFactory接口实现:DefaultForkJoinWorkerThreadFactory。
-
handler:异常捕获处理器。当执行的任务中出现异常,并从任务中被抛出时,就会被handler捕获。
-
asyncMode:这个参数也非常重要,从字面意思来看是指的异步模式,它并不是说Fork/Join框架是采用同步模式还是采用异步模式工作。Fork/Join框架中为每一个独立工作的线程准备了对应的待执行任务队列,这个任务队列是使用数组进行组合的双向队列。即是说存在于队列中的待执行任务,即可以使用先进先出的工作模式,也可以使用后进先出的工作模式。当asyncMode设置为ture的时候,队列采用先进先出方式工作;反之则是采用后进先出的方式工作,该值默认为false
使用Fork/Join框架进行归并排序:
阻塞队列(BlockingQueue)
栈:后进先出
队列:先进先出
阻塞队列:

当队列是空的,从队列中获取元素的操作将会被堵塞
当队列是满的,向队列中添加元素的操作将会被堵塞
试图从空的队列中获取元素的线程将会被堵塞,直到其他线程向空的队列中插入新的元素
试图向满的队列中添加元素的线程将会被堵塞,直到其他线程从满的队列中获取新的元素
堵塞:在多线程领域,所谓的堵塞其实就是在某些情况下,线程会挂起,一旦条件满足,被挂起的线程又会被自动唤起。

ArrayBlockingQueue:由数组结构组成的有界阻塞队列
LinkedBlockingQueue:由链表数据结构组成的有界的(大小默认为integer.MAX_VALUE)阻塞队列
DelayQueue:使用优先级队列实现的延迟无界堵塞队列
PriorityBlockingQueue:支持优先级排序的无界阻塞队列
SynchronousQueue:不纯出元素的阻塞队列,也即单个元素的队列
LinkedTransferQueue:由链表组成的无界堵塞队列
LinkedBlockingDeque:由链表组成的双向堵塞队列
BlockingQueue核心方法:

浙公网安备 33010602011771号