JUC内容概述

news2024/11/14 6:32:07

复习概念

Sleep和Wait的区别

  1. Sleep是Thread的静态方法,wait是Object的方法,任何对象实例都可以使用
  2. sleep不会释放锁,他也不需要占用锁,暂停。wait会释放锁,但是调用他的前提是线程占有锁
  3. 他们都可以被Interrupted方法中断。在哪里停,在哪里起

并发并行

并发:同一时刻,多个线程访问同一资源

并行:多项工作一起执行,之后汇总

线程的状态

Thread.status

管程

monitor监视器 所说的锁

是一种同步机制,保证同一时间,只有一个线程 访问被保护的数据或者代码

用户线程和守护线程

用户线程:自定义线程,平时用到的具体逻辑

守护线程:垃圾回收线程

主线程已经结束了,用户线程还在运行,jvm存活、

如果程序中没有用户线程了,守护线程也会结束,在执行start之前设置是否是守护线程。

卖票程序:

package com.policy.thread;

class Ticket{
    private int ticket =30;

    public synchronized void sale(){
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"卖出:"+(ticket--)+"剩余:"+ticket);
        }
    }
}

public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        },"a").start();

        new Thread(new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        },"b").start();

        new Thread(new Runnable() {
            @Override
            public void run() {

                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        },"c").start();
    }

}

可重入锁

Lock.lock()

Lock.unlock()

lock不是java内置的,synchronized是java关键字

sync不需要手动释放锁,lock上锁之后需要手动释放锁

package com.policy.thread;

import java.util.concurrent.locks.ReentrantLock;

class LTicket{
    private int num=30;
    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();
    //卖票方法
    public void sale(){
        //上锁
        lock.lock();
        try {
            if(num>0){
                System.out.println(Thread.currentThread().getName()+"卖出"+(num--)+"剩余"+num);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //解锁
            lock.unlock();
        }

    }
}

public class LTicketSale {

    public static void main(String[] args) {
        LTicket lTicket = new LTicket();

        //创建线程 表达式
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                lTicket.sale();
            }
        },"a").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                lTicket.sale();
            }
        },"b").start();
        new Thread(()->{
            for (int i = 0; i < 40; i++) {
                lTicket.sale();
            }
        },"c").start();

    }

}

Lock 与Sync总结

1、Lock是一个接口,sync是java的关键字,是一种内置的实现语言

2、当sync发生异常时,会自动释放占有的锁,因此不会导致死锁的发生;

Lock在发生异常时,如果没有主动通过unLock释放锁,则很可能会造成死锁现象,因此在Finally中释放锁是很必要的。

3、Lock会让等待锁的线程响应中断,而sync却不会中断等待线程,使用sync会一直等待下去,不能够响应中断。

4、通过lock可以知道有没有成功获取锁,而Sync却无法办到。

5、Lock可以提高多个线程读操作的效率。

线程通信

第一步:创建资源类,在资源类中创建属性和方法

第二步:方法中

判断、干活、通知

第三步:创建多个线程,调用资源类的操作方法

第四步:防止虚假唤醒问题

package com.policy.thread.Thread202308;
class Share{

    //线程等待和唤醒
    private int num=0;

    public synchronized void incr() throws InterruptedException {
        //加一操作
        if(num!=0){
            //不等于0说明不需要加一 等待
            this.wait();
        }
        //加一
        num++;
        System.out.println(Thread.currentThread().getName()+"::"+num);
        //唤醒其他线程 可以减一了
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        if(num!=1){
            //等待
            this.wait();
        }
        //减一
        num--;
        System.out.println(Thread.currentThread().getName()+"::"+num);
        this.notifyAll();
    }
}

public class ThreadDemo01 {
    public static void main(String[] args) {
        Share share=new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        },"aa").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        },"bb").start();
    }

}

此方法 如果多了线程之后 增加CCDD线程执行操作会有虚假唤醒的问题,问题的原因是出现在判断条件中

A释放之后 BCD都有机会抢到,C抢到了C也会等待,然后再去让其他线程抢,继续唤醒A线程,WAIT有一个问题,就是在哪等待,唤醒之后在哪执行,那么会执行if下面的语句,第二次执行A可能会继续执行+1

解决:把判断放到while循环中,无论什么时候唤醒,都会在执行循环的判断条件。

package com.policy.thread.Thread202308;
class Share{

    //线程等待和唤醒
    private int num=0;

    public synchronized void incr() throws InterruptedException {
        //加一操作
        while(num!=0){
            //不等于0说明不需要加一 等待
            this.wait();
        }
        //加一
        num++;
        System.out.println(Thread.currentThread().getName()+"::"+num);
        //唤醒其他线程 可以减一了
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        while(num!=1){
            //等待
            this.wait();
        }
        //减一
        num--;
        System.out.println(Thread.currentThread().getName()+"::"+num);
        this.notifyAll();
    }
}

public class ThreadDemo01 {
    public static void main(String[] args) {
        Share share=new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        },"aa").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        },"bb").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        },"cc").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        },"dd").start();
    }

}

Lock实现上锁和线程通信

lock.lock();上锁

lock.unlock();解锁

condition.await();等待

condition.signalAll();唤醒其他线程

package com.policy.thread.Thread202308;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Share02{
    //创建锁
    private Lock    =new ReentrantLock();
    Condition condition = lock.newCondition();
    private int num = 0;
    public void incr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            while (num!=0){
                //通信等待
                condition.await();
            }
            //干活
            num++;
            System.out.println(Thread.currentThread().getName()+"::"+num);
            condition.signalAll();
        } finally {
            //解锁
            lock.unlock();
        }
        //判断 干活 通信
    }

    public void decr() throws InterruptedException {
        //上锁
        lock.lock();
        try {
            while (num!=1){
                //通信等待
                condition.await();
            }
            //干活
            num--;
            System.out.println(Thread.currentThread().getName()+"::"+num);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
        //判断 干活 通信
    }
}

public class ThradDemo02 {
    public static void main(String[] args) {
        Share02 share02 = new Share02();

        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    share02.incr();
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"A").start();
        new Thread(()->{
            try {
                share02.decr();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"B").start();
        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    share02.decr();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"C").start();
        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    share02.decr();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"D").start();

    }
}

线程定制化通信

以上不管使用那种方式上锁或者进行通信 但是都不能将数据进行有顺序的固定化的通信

例如 指定三个线程,按照要求执行

AA打印5次 BB打印10次 CC打印15次

进行十轮

package com.policy.thread.Thread202308;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ShareResource{
    private int falg =1;//1表示A的 2-B 3C
    //创建锁
    private Lock lock =new ReentrantLock();

    //创建三个condition
    private Condition c1= lock.newCondition();
    private Condition c2 = lock.newCondition();
    private Condition c3=lock.newCondition();

    public void print5(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (falg!=1){
                //等待 不打印A
                c1.await();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+"当前是第轮:"+loop);
            }
            //通知
            falg=2;//修改flag标示位的值
            c2.signal();//此时在A线程中 唤醒的是C2
        } finally {
            lock.unlock();
        }
    }

    public void print10(int loop) throws InterruptedException {
        lock.lock();//上锁
        try {
            //等待
            while (falg!=2){
                c2.await();
            }
            //干活
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+"当前是第轮:"+loop);
            }
            //通知
            falg=3;
            c3.signal();
        } finally {
            lock.unlock();
        }
    }

    public void print15(int loop) throws InterruptedException {
        lock.lock();//上锁
        try {
            //等待
            while (falg!=3){
                c3.await();
            }
            //干活
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+"当前是第轮:"+loop);
            }
            //通知
            falg=1;
            c1.signal();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo3 {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print5(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print10(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print15(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"C").start();
    }
}

集合的线程安全

集合不安全的情况

List线程不安全的例子

在一边向list中add 一边需要输出list时 有可能会报错:并发修改异常

Exception in thread "2" Exception in thread "5" Exception in thread "1" Exception in thread "4" java.util.ConcurrentModificationException

package com.policy.thread.Thread202308;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class ThreadDemo04 {
    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));

                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

  

解决方案:

list之所以会报错并发修改异常 原因是list并没有加上synchronized关键字修饰

1、Vcetor 少用

Vector 的方法上 都加上了synchronized

package com.policy.thread.Thread202308;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;

public class ThreadDemo04 {
    public static void main(String[] args) {

        List<String> list = new Vector<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));

                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

2、Collections

List<String> list = Collections.synchronizedList(new ArrayList<>());

通过调用Collections中的synchronized方法 即可将new出的List变成线程安全的

一般也少用

3、CopyOnWriteArrayList

List<String> list =new CopyOnWriteArrayList<>();

写时复制技术

可以并发的读取list的内容,但是需要向list写入的时候,需要复制一份写入新内容,然后将两份合并起来,读取合并后的新内容。兼顾了并发读 和写的操作

HashSet不安全的情况

Set<String> set =new HashSet<>();
for (int i = 0; i < 30; i++) {
    new Thread(()->{
        set.add(UUID.randomUUID().toString().substring(0,8));
        System.out.println(set);
    },String.valueOf(i)).start();
}

Exception in thread "5" Exception in thread "17" Exception in thread "21" Exception in thread "23" Exception in thread "24" Exception in thread "28" java.util.ConcurrentModificationException

解决方法

CopyOnWriteArraySet

Set<String> set = new CopyOnWriteArraySet<>();

HashMap不安全的情况

Map<String,String> map =new HashMap<>();
for (int i = 0; i < 30; i++) {
    new Thread(()->{
        map.put(UUID.randomUUID().toString().substring(0,8),"1111");
        System.out.println(map);
    },String.valueOf(i)).start();
}

解决方案

Map<String,String > map =new ConcurrentHashMap<>();

多线程锁

synchronized实现同步方法的基础:java每个对象都可以作为锁,具体的表现有三种形式。

对于普通的同步方法,锁的是当前实例的对象

对于静态同步方法,锁的是当前类的Class

对于同步方法块,锁的是Synchronized括号里面的配置对象

公平锁和非公平锁

非公平锁:线程会饿死,但是执行效率高

公平锁:阳光普照,效率相对低

可重入锁

synchronized 和Lock都是可重入锁

syn是一种隐式的可重入锁,Lock是显示的可重入锁 可重入锁也叫递归锁

死锁

两个或者两个以上的进程在执行过程中,因为争夺资源而造成的一种互相等待的现象,如果没有外力干涉他们就无法执行下去的现象、

产生死锁的原因:

1、系统资源不足

2、进程运行的推进顺序不合适

3、资源分配不当

package com.policy.thread.Thread202308;

import java.sql.Time;
import java.util.concurrent.TimeUnit;

public class DeadLock {

    //创建两个对象
    static Object a = new Object();
    static Object b = new Object();

    public static void main(String[] args) {
        new Thread(()->{
            synchronized (a){
                System.out.println(Thread.currentThread().getName()+"持有A,试图获取B");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (b){
                    System.out.println(Thread.currentThread().getName()+"获取B");
                }
            }
        },"A").start();

        new Thread(()->{
            synchronized (b){
                System.out.println(Thread.currentThread().getName()+"持有B,试图获取A");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (a){
                    System.out.println(Thread.currentThread().getName()+"获取A");
                }
            }
        },"B").start();
    }
}

验证死锁

1、jps 类似于:linux ps -ef

2、jstack :jvm自带的堆栈跟踪工具

Callable接口

Callable接口可以提供返回值

Runnable和Callable 区别

run call

无返回值 有返回值

无异常抛出 有异常抛出

package com.policy.thread.Thread202308.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//实现runnable接口
class Mythread1 implements Runnable{

    @Override
    public void run() {

    }
}

class Mythread2 implements Callable{

    @Override
    public Object call() throws Exception {
        return 200;
    }
}

public class Demo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //调用runnable接口
        Mythread1 mythread1 = new Mythread1();
        Thread thread = new Thread(mythread1);
        thread.setName("a");
        thread.start();

        //调用callable
        //FutureTask是runnable的实现接口
        //future 构造方法可以传递Callable
        FutureTask<Integer> futureTask = new FutureTask<>(new Mythread2());
        //以上的代码 可以用lambda表达式做简化
        FutureTask<Integer> futureTask1 = new FutureTask<>(()->{
            return 1024;
        });

        //FutureTask 原理 为什么叫未来任务

        new Thread(futureTask1).start();
        System.out.println(futureTask1.get());
    }


}

Future优缺点

优点:future结合线程池异步多任务配合,能显著的提高程序的执行效率。

package com.policy.thread.JUC;

import java.util.concurrent.*;

public class FutureThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        long start = System.currentTimeMillis();
        //线程池

        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        //开启多个异步任务 处理
        FutureTask<String> task1 = new FutureTask<>(() -> {
            TimeUnit.MILLISECONDS.sleep(500);
            return "task1 over";
        });

        FutureTask<String> task2 = new FutureTask<>(() -> {
            TimeUnit.MILLISECONDS.sleep(300);
            return "task2 over";
        });

        FutureTask<String> task3 = new FutureTask<>(() -> {
            TimeUnit.MILLISECONDS.sleep(300);
            return "task3 over";
        });
        threadPool.submit(task1);
        threadPool.submit(task2);
        threadPool.submit(task3);

        //获取结果
        System.out.println(task1.get());
        System.out.println(task2.get());
        System.out.println(task3.get());
        threadPool.shutdown();

        long end = System.currentTimeMillis();
        System.out.println(end-start+"毫秒");
        System.out.println(Thread.currentThread().getName()+"执行结束");
    }

    private static void m1() throws InterruptedException {


        long start = System.currentTimeMillis();
        TimeUnit.MILLISECONDS.sleep(500);
        TimeUnit.MILLISECONDS.sleep(300);
        TimeUnit.MILLISECONDS.sleep(300);
        long end = System.currentTimeMillis();
        System.out.println(end-start+"毫秒");
        System.out.println(Thread.currentThread().getName()+"执行结束");
    }
}

缺点 FutureTask的缺点、

get方法会阻塞:

package com.policy.thread.JUC;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class FutureAPIDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> stringFutureTask = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + "come in!");
            TimeUnit.SECONDS.sleep(5);
            return "task over";
        });

        Thread thread = new Thread(stringFutureTask);
        thread.start();
        System.out.println(stringFutureTask.get());//非要等到结果 才会执行
        //由于会在上面get不到结果 需要等五秒之后才能get到  所以 线程会阻塞五秒 才能执行下面的输出
        System.out.println("main over");
        
    }
}

1、get容易阻塞,一般放到最后才执行

2、如果不愿放到最后,我希望过时不候,可以自动离开

System.out.println(stringFutureTask.get(3,TimeUnit.SECONDS));

最后超过时间会抛出异常

JUC的辅助类

1、减少计数的CountDownLatch

CountDownLatch主要有两个方法,当一个或者多个线程调用 await方法时,这个线程会阻塞。

调用CountDown方法会将计数器减一。当计数器变为0时,因await方法阻塞的线程会被唤醒,继续执行。

2、循环栅栏CyclicBarrier

3、信号灯Semaphore

读写锁

悲观锁

不支持并发操作

乐观锁

表锁

操作表中的一行的记录或者数据 整张表都上锁

行锁

操作表中第一行的数据,只对第一行上锁,其他线程可以操作其他行的数据

读锁

共享锁

写锁

独占锁

读写锁都有可能发生死锁的状况

读锁发生死锁的状况

读写锁的例子,如果不加上读写锁,那么在还没有写完时就会执行读取的操作,

加锁之后,写完才可以读,并且可以看到结果中写操作是独占的,读操作是共享的。

读写锁:一个资源可以被多个读操作线程访问,一个资源只能被一个写操作线程访问,一个资源不能同时存在读写线程访问,读写互斥,写写互斥,读读共享。

无锁时,多个线程共 全的情况发生。

添加锁synchronized或者Lock这样就变成了线程独占的了,资源不共享,变成单线程的了

添加读写锁ReentrantReadWriteLock:相较与上面的锁,读读操作变成了共享的了,提升性能,多线程可共同操作(读)一个资源

缺点:造成 锁饥饿,一直读没有写操作、 读的时候不能写,读完了才可以写,写的时候可以读。=会造成锁降级的过程

锁降级

写入锁降级为读锁的过程

jdk8 中锁降级的过程:

阻塞队列

BlockingQueue阻塞队列

当队列为空的时候,从对列中获取元素,只能阻塞

当队列是满的时候,向队列中添加元素,阻塞

好处是,一系列的判断是否为空,满的操作,都是自动完成的。我们不需要关心什么时候需要挂起线程,什么时候需要唤醒线程。

这些都是阻塞队列封装好的。

阻塞队列的架构

ArrayBlockingQueue:基于数组实现的阻塞队列,内部维护了一个定长的数组,以便缓存队列的数据对象。

LinkedBlockingQueue:由链表组成的阻塞队列

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

PriorityBlockQueue:支持优先级排序的队列

SynchronousQueue:无缓冲的阻塞队列,不存储元素的的阻塞队列,只存单个元素‘

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

LinkedBlockDeque:有链表组成的双向阻塞队列

add、remove这一组的话,在长度范围内正常操作,add会返回true或者false 超过之后会抛出异常

offer和poll offer满了之后添加不会抛异常,会返回false 队列空了之后,执行poll,会进行返回null 不会抛异常

put 当队列满了之后会一直阻塞在那里,take也是,队列空了之后不会结束也不会完成 会停留在那里

第四组的offer poll设置超时时间,在时间范围内阻塞 超时就会退出 offer会返回false

ThreadPool线程池

一种线程使用模式,线程过多会带来调度的开销,进而影响缓存局部的和整体的性能。线程池维护多个线程,等待着监督管理者分配可并发执行的任务。

优点:降低资源消耗,提高响应速度。提搞可管理性

线程池的分类:

Executors.newFixedThreadPool(int)一个线程池,里面多个线程

Executors.newSingleThreadExecutor()一个线程池一个线程

Executors.newCachedThreadPool()缓存线程池,理论上可无限扩容

举例

newFixedThreadPool

package com.policy.thread.Thread202308.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //fixed
        ExecutorService threadpool1 = null;//5个窗口
        try {
            threadpool1 = Executors.newFixedThreadPool(5);
            for (int i = 0; i < 10; i++) {
                //执行
                threadpool1.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            threadpool1.shutdown();
        }
    }
}

newSingleExecutor()

package com.policy.thread.Thread202308.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //fixed
        ExecutorService threadpool1 = Executors.newFixedThreadPool(5);//5个窗口

        ExecutorService threadpool2 = Executors.newSingleThreadExecutor();//一个线程

        try {

            for (int i = 0; i < 10; i++) {
                //执行
                threadpool2.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            threadpool2.shutdown();
        }
    }
}

相当于开启了一个线程 只输出一个线程名

newCacheThreadPool()

package com.policy.thread.Thread202308.pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo1 {
    public static void main(String[] args) {
        //fixed
        ExecutorService threadpool1 = Executors.newFixedThreadPool(5);//5个窗口

        ExecutorService threadpool2 = Executors.newSingleThreadExecutor();//一个线程

        ExecutorService threadpool3 = Executors.newCachedThreadPool();

        try {

            for (int i = 0; i < 10; i++) {
                //执行
                threadpool3.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");
                });
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            threadpool3.shutdown();
        }
    }
}

执行结果:

可以看到此处执行最多创建了7个线程,来执行一个循环任务,相当于开了七个窗口,理论上可扩充更灵活

底层实现

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

 三个线程池的底层都是new ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

7个线程池参数:

int corePoolSize:常驻的核心线程的数量

int maximumPoolSize :最大的线程数量

long keepAliveTime, TimeUnit unit,存活时间 及存活时间的单位,非核心线程的空闲的存活时间,超出时间就会关闭掉

BlockingQueue<Runnable> workQueue,常驻的线程数量用完之后,进入阻塞队列

ThreadFactory threadFactory,线程工厂,用于创建线程

RejectedExecutionHandler handler拒绝策略

线程池的工作流程以及7个参数的使用

大致的流程图

执行了线程池的execute之后,线程池才会创建,

以上的图中核心线程数2 最大线程数5 阻塞队列数3

当有一个线程进到线程池之后,会最先进入核心线程中执行,如果核心线程此时已满,会加到阻塞队列中,如果阻塞队列长度也满了,那么会创建核心线程池之外的线程进行执行。如果最大线程池也满了,会根据线程策略进行操作

拒绝策略jdk内置的拒绝策略

AbortPolicy(默认) :直接抛出RejectedExecutionException异常,阻止系统正常运行。

CallerRunsPolicy:调用者运行,一种调节机制,该策略不会抛弃任务,也不会抛出异常,而是将任务回退给调用者,降低任务流量

DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中尝试再次提交当前任务

DiscardPolicy:该策略默默的丢弃无法处理的任务,不予以任何处理也不抛出异常。如果允许丢失任务,这是最好的策略

自定义线程池

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor传参数的方式进行创建

原因1允许阻塞队列的请求长度是Integer.MAX_VALUE,可能会造成大量的线程堆积。导致OOM

实际中自己自定义线程池进行操作

Fork/join分支合并框架

可以将一个大任务,拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果进行输出。

FORK:把一个任务进行拆分,大事化小

Join:把拆分任务结果进行合并

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1551874.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

构建一个包含mvn命令的Java 17基础镜像

前言 官方提供的openjdk基础镜像&#xff0c;不包含mvn命令&#xff0c;无法用容器来打包代码。 在官方提供的镜像基础上安装maven。 前期准备&#xff0c;需要安装好docker。 一、安装maven 1、下载openjdk基础镜像&#xff0c;执行如下代码。 docker pull openjdk:17-j…

Git命令上传本地项目至github

记录如何创建个人仓库并上传已有代码至github in MacOS环境 0. 首先下载git 方法很多 这里就不介绍了 1. Github Create a new repository 先在github上创建一个空仓库&#xff0c;用于一会儿链接项目文件&#xff0c;按照自己的需求设置name和是否private 2.push an exis…

iOS客户端自动化UI自动化airtest+appium从0到1搭建macos+脚本设计demo演示+全网最全最详细保姆级有步骤有图

Android客户端自动化UI自动化airtest从0到1搭建macos脚本设计demo演示全网最全最详细保姆级有步骤有图-CSDN博客 避坑系列-必读&#xff1a; 不要安装iOS-Tagent &#xff0c;安装appium -这2个性质其实是差不多的都是为了安装wda。注意安装appium最新版本&#xff0c;安装完…

Mysql的日志管理,备份与回复

目录 一、Mysql日志管理 1、日志的默认位置及配置文件 2、日志分类 2.1错误日志 2.2通用查询日志 2.3二进制日志 2.4慢查询日志 2.5中继日志 3、日志配置 4、日志查询 4.1查询通用日志是否开启 4.2查询二进制日志是否开启 4.3查看慢查询日志是否开启 4.4查询慢查…

损坏的RAID5csp

1.解题思路 这道题太抽象了&#xff0c;一开始都没太搞懂在讲啥。。。解决该题需要了解条带、磁盘号的定义。 下图以样例2&#xff0c;输入编号为5的块为例&#xff1a; 请务必加上ios::sync_with_stdio(false),否则会超时只有30分 2.满分代码 #include<iostream> us…

C++进阶之路---C++11新特性 | lambda表达式

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 前言&#xff1a;简介lambda 在C中&#xff0c;lambda表达式是一种匿名函数的方式&#xff0c;它可以用来解决以下问题&a…

2023年蓝桥杯省赛——矩形面积总和

目录 题目链接&#xff1a; 1.矩形总面积 - 蓝桥云课 (lanqiao.cn) 思路 暴力 数学杯思路 数学逻辑 难点之重合区域 代码实现 总结 题目链接&#xff1a; 1.矩形总面积 - 蓝桥云课 (lanqiao.cn) 思路 暴力 开幕雷击&#xff0c;我直接开始暴力&#xff0c;但是我暴力…

深入解析RSA算法原理及其安全性机制

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 一、RSA算法简介二、RSA算法原理2.1 背景与数学基础2.2 密钥生成2.3 加密过程2.4 解密过程 三、安全性考虑四、RSA的使用五、…

【Linux】详解进程程序替换

一、替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支)&#xff0c;子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时&#xff0c;该进程的用户空间代码和数据完全被新程序替换&#xff0c;从新程序的启动例程开始执…

爱上数据结构:顺序表和链表

一、线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条…

iOS - LLVM的中间代码(IR)

文章目录 iOS - LLVM的中间代码&#xff08;IR&#xff09;1. 转为汇编代码2. 中间代码&#xff08;IR&#xff09;2.1 Objective-C在变为机器代码之前&#xff0c;会被LLVM编译器转换为中间代码&#xff08;Intermediate Representation&#xff09;2.2 可以使用以下命令行指令…

MySQL 索引:索引为什么使用 B+树?

Hash 索引不支持顺序和范围查询&#xff1b; 二叉查找树(BST)&#xff1a;解决了排序的问题&#xff0c;极端情况下可能会退化成线性链表&#xff0c;查询效率急剧下降&#xff1b; 平衡二叉树(AVL) &#xff1a;通过旋转解决了平衡的问题&#xff0c;但是旋转操作效率太低&am…

蓝桥杯练习题总结(三)线性dp题(摆花、数字三角形加强版)

目录 一、摆花 思路一&#xff1a; 确定状态&#xff1a; 初始化&#xff1a; 思路二&#xff1a; 确定状态&#xff1a; 初始化&#xff1a; 循环遍历&#xff1a; 状态转移方程&#xff1a; 二、数字三角形加强版 一、摆花 题目描述 小明的花店新开张&#xff0c;为了吸…

spring boot3自定义注解+拦截器+Redis实现高并发接口限流

⛰️个人主页: 蒾酒 &#x1f525;系列专栏&#xff1a;《spring boot实战》 &#x1f30a;山高路远&#xff0c;行路漫漫&#xff0c;终有归途 目录 写在前面 内容简介 实现思路 实现步骤 1.自定义限流注解 2.编写限流拦截器 3.注册拦截器 4.接口限流测试 写在前…

戴尔电脑Dell SupportAssist占用内存高,卸载Dell SupportAssist

咨询戴尔客服了解到&#xff0c;SupportAssist是机器出厂自带的一款应用&#xff0c;主要的功能是可以检查驱动更新以及做一些硬件方面的健康检测&#xff0c;有时候后台运行可能会导致进程占用内存比较大&#xff0c;导致访问被的应用崩溃。 咨询卸载不影响之后&#xff0c;然…

在CentOS7上部署Nginx并测试指南

Nginx部署测试 Nginx简介 Nginx是俄罗斯人Igor Sysoev编写的一款高性能的HTTP和反向代理服务器。 Nginx选择了epoll和kqueue作为网络I/O模型&#xff0c;在高连接并发的情况下&#xff0c;内存、CPU等系统资源消耗非常低&#xff0c;运行稳定。 正向代理与反向代理 正向代…

FLStudio多少钱FL Studio中文版软件序列号-激活码购买

fl studio是一款编曲软件&#xff0c;接触这款软件的大多都是做音乐的小伙伴吧&#xff0c;对于初学者想了解这款软件在意的应该就是它的价格。很多打算入手正版FL Studio的新手朋友都会纠结一个问题&#xff1a;哪个版本的FL Studio更适合我&#xff0c;到底应该入手哪一款FL …

Java安全篇-Fastjson漏洞

前言知识&#xff1a; 一、json 概念&#xff1a; json全称是JavaScript object notation。即JavaScript对象标记法&#xff0c;使用键值对进行信息的存储。 格式&#xff1a; {"name":"wenda","age":21,} 作用&#xff1a; JSON 可以作为…

OCP NVME SSD规范解读-15.DSSD set feature功能要求-2

启用IEEE1667隔离区(Enable IEEE1667 Silo)&#xff1a;特征标识符C4h允许开启符合IEEE1667标准的安全存储区功能&#xff0c;以实现数据的隔离和安全存储。 4.15.9章节描述了启用IEEE1667 Silo&#xff08;通过Feature Identifier C4h标识的Set Feature命令&#xff09;的相关…

C++:转义符(10)

在c中有一些字符无法被显示出来&#xff0c;所以需要使用些特殊字符加字母来展示 可以看到基本都是一个\加一个字母去只执行对应的一个效果 这里我选择几个对于当前来说比较重要的&#xff1a;\n &#xff0c;\\ &#xff0c;\t \n换行符 可以看到在c语言中他就是一个可以换行…