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);
    }
}

image-20211009174205040

此时十分清晰的出现,当测试次数足够多的时候,会出现判断不生效的情况(虚假唤醒)

虚假唤醒其实就是我们编程的错误,产生这种错误的原因就是,我们不知道

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方法之后的语句。

image-20211011160206399

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方法。

image-20211011161930134

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();
        }
    }
}

image-20211011163238517

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

Semaphore

image-20211011164822987

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();
        }
    }
}

image-20211011165846886

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

image-20211011171202000

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;
        }
    }

}

我们可以观察一下执行结果:

如果我们没有加上读写锁:

image-20211011200958344

加上读写锁:

image-20211011201058044

读写锁降级(JDK8):

image-20211011202240813

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();
}

image-20211011204124621

读锁不能升级为写锁。

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();
}

image-20211011204325136

然后不会进行写操作的

乐观锁和悲观锁

image-20211011170129931

不支持并发操作

image-20211011170144476

支持并发操作,修改后不仅仅修改操作数据,也要将版本号一起修改,当然在你每次读的时候都要获取版本号,以便于你修改的时候方便进行版本号的比对

表锁和行锁

表锁:操作这张表的时候整个表都上锁,其他线程不可以进行操作。

行锁:操作某一行的时候,只对这一行进行锁定

行锁是会发生死锁的。

线程池

提前创建号多个线程,放入线程池中,使用的时候直接获取,使用完放回线程池中。可以避免频繁创建和销毁线程,实现重复使用。

优势:

1、提高响应速度

2、降低资源损耗

3、便于线程管理

jdk5.0之后提供了线程池支持,并且提供了相关API:ExecutorService和Executors

image-20211011215316168

几种常用的线程池:

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();
    }
}

image-20211011221131351

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();
    }
}

image-20211011221225260

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();
    }
}

image-20211011221259572

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:拒绝策略

工作流程和拒绝策略

image-20211011222724816

注意点:

当我们创建出线程池的时候(Executors.newCachedThreadPool()等等操作的时候)其实没有创建线程,只有当执行execute方法的时候线程才会被创建。

上图中,我们假设 常驻线程数=3 最大线程数=5 阻塞队列长度=3。

1-2由常驻线程执行 3-5进入堵塞队列 6-8由线程池创建新线程执行

9。。。。。。拒绝策略

拒绝策略:

image-20211011223210259

自定义线程池:

image-20211011223417764

实际开发中我们一般是自定义线程池进行使用

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();
    }
}

image-20211011223804826

执行了拒绝策略

分支合并框架Fork/Join

image-20211012154300978

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;
    }
}

image-20211012155622782

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)

栈:后进先出

队列:先进先出

阻塞队列:

image-20211011212707728

当队列是空的,从队列中获取元素的操作将会被堵塞

当队列是满的,向队列中添加元素的操作将会被堵塞

试图从空的队列中获取元素的线程将会被堵塞,直到其他线程向空的队列中插入新的元素

试图向满的队列中添加元素的线程将会被堵塞,直到其他线程从满的队列中获取新的元素

堵塞:在多线程领域,所谓的堵塞其实就是在某些情况下,线程会挂起,一旦条件满足,被挂起的线程又会被自动唤起。

image-20211011213813465

ArrayBlockingQueue:由数组结构组成的有界阻塞队列

LinkedBlockingQueue:由链表数据结构组成的有界的(大小默认为integer.MAX_VALUE)阻塞队列

DelayQueue:使用优先级队列实现的延迟无界堵塞队列

PriorityBlockingQueue:支持优先级排序的无界阻塞队列

SynchronousQueue:不纯出元素的阻塞队列,也即单个元素的队列

LinkedTransferQueue:由链表组成的无界堵塞队列

LinkedBlockingDeque:由链表组成的双向堵塞队列

BlockingQueue核心方法:

image-20211011214631323

posted on 2021-10-16 17:30  gyp666  阅读(141)  评论(0)    收藏  举报