JUC内容概述

复习概念

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.mfbz.cn/a/496841.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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 可以使用以下命令行指令…

html音频和视频可输入表单input

音频和视频 loop循环播放autoplay自动播放controls显示控制面板<audio src""> //<video src"#">muted静音播放 可输入表单input password密码框 radio单选框 checkbox复选框 file上传文件 text文本框 文本框<input type"text"…

网络编程综合项目-多用户通信系统

文章目录 1.项目所用技术栈本项目使用了java基础&#xff0c;面向对象&#xff0c;集合&#xff0c;泛型&#xff0c;IO流&#xff0c;多线程&#xff0c;Tcp字节流编程的技术 2.通信系统整体分析主要思路&#xff08;自己理解&#xff09;1.如果不用多线程2.使用多线程3.对多线…

智能车主控板原理图原理讲解

智能车主控板原理图原理讲解 综述&#xff1a;本篇文章对智能车主控板的一部分电路进行原理分析&#xff0c;文末附加整体原理图。 1. 电源电路 &#xff08;1&#xff09;通过外接电池供电并通过电源模块电路&#xff0c;运用稳压芯片lm2940&#xff0c;将电源电压转化为5V…

原生JS上传大文件分片

代码&#xff1a;https://gitee.com/xproer/up6-vue-cli 1.引入up6组件 2.配置接口地址 接口地址分别对应&#xff1a;文件初始化&#xff0c;文件数据上传&#xff0c;文件进度&#xff0c;文件上传完毕&#xff0c;文件删除&#xff0c;文件夹初始化&#xff0c;文件夹删除&…

市场复盘总结 20240328

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 40% 最常用的…

代码随想录算法训练营第day60|84.柱状图中最大的矩形

84.柱状图中最大的矩形 力扣题目链接(opens new window) 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。 求在该柱状图中&#xff0c;能够勾勒出来的矩形的最大面积。 思路&#xff1a; 为什么这么说呢&#xff…

第三十二天-PythonWeb主流框架-Django框架

目录 1.介绍 发展历史 介绍 2.使用 1.安装 2.创建项目 3.项目结构 4.启动 3.开发流程 1.设置ip可访问 2.创建模块 3.第一个页面 4.视图 5.include()参数 6.url与视图的关系 7.响应内容 4.视图处理业务逻辑 1.响应html 2.获取url参数 3.从文件响应html内容 …

趣味物理知识竞赛活动方案

为了丰富校园文化生活&#xff0c;创建格调高雅、学习氛围浓厚、青春气息浓郁的校园文化&#xff0c;注重多样性全方面的发展&#xff0c;推进了素质教育向纵深拓展&#xff0c;全面提升大学生的综合素质和精神文明建设全面发展。为进一步引导大学生了解前沿科技&#xff0c;普…

24计算机考研调剂 | 【官方】北京科技大学

北京科技大学 考研调剂招生信息 招生专业&#xff1a; 085404&#xff08;计算机技术&#xff09; 081200&#xff08;计算机科学与技术&#xff09; 调剂要求&#xff1a;&#xff08;调剂基本分数&#xff09; 我中心将在教育部“全国硕士生招生调剂服务系统”&#xff08…

面试算法-124-二叉树的最近公共祖先

题目 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它…

缓存雪崩问题及解决思路

实战篇Redis 2.7 缓存雪崩问题及解决思路 缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 解决方案&#xff1a; 给不同的Key的TTL添加随机值利用Redis集群提高服务的可用性给缓存业务添加降…

Requests教程-20-sign签名认证

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节中&#xff0c;我们学习了requests的token认证方法&#xff0c;本小节我们讲解一下requests的sign签名认证。 在进行接口调用时&#xff0c;一般会要求进行签名操作&#xff0c;以确保接口的安全性和完整…

基于springboot实现课程作业管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现课程作业管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;课程作业管理系统当然也不能排除在外。课程作业管理系统是以实际运用为开发背景…

项目实战:电影评论情感分析系统

目录 1.引言 2.数据获取与预处理 3.构建文本分类模型&#xff08;使用LSTM&#xff09; 4.结果评估与模型优化 4.2.结果评估 4.2.模型优化 5.总结 1.引言 在本篇文章中&#xff0c;将通过一个完整的项目实战来演示如何使用Python构建一个电影评论情感分析系统。涵盖从数…

OSCP靶场--pyLoader

OSCP靶场–pyLoader 考点(信息收集CVE-2023-0297) 1.nmap扫描 ┌──(root㉿kali)-[~/Desktop] └─# nmap -Pn -sC -sV 192.168.178.26 --min-rate 2500 Starting Nmap 7.92 ( https://nmap.org ) at 2024-03-28 09:14 EDT Nmap scan report for 192.168.178.26 Host is up…

二分图

数据结构、算法总述&#xff1a;数据结构/算法 C/C-CSDN博客 二分图&#xff1a;节点由两个集合组成&#xff0c;且两个集合内部没有边的图。换言之&#xff0c;存在一种方案&#xff0c;将节点划分成满足以上性质的两个集合。 染色法 目的&#xff1a;验证给定的二分图是否可…

房间采光不好怎么改造?这里有6个实用的解决方案!福州中宅装饰,福州装修

当装修中遇到房间采光不好的问题&#xff0c;可以从以下几个方面来解决&#xff1a; ①引入自然光源 尽可能减少光线阻碍物&#xff0c;例如可以考虑打通一些非承重墙&#xff0c;扩大窗户的面积&#xff0c;让阳光直接穿过阳台照射到室内。同时&#xff0c;也可以考虑在某些没…

YOLOV8逐步分解(2)_DetectionTrainer类初始化过程

接上篇文章yolov8逐步分解(1)--默认参数&超参配置文件加载继续讲解。 1. 默认配置文件加载完成后&#xff0c;创建对象trainer时&#xff0c;需要从默认配置中获取类DetectionTrainer初始化所需的参数args&#xff0c;如下所示 def train(cfgDEFAULT_CFG, use_pythonFalse…

17.注释和关键字

文章目录 一、 注释二、关键字class关键字 我们之前写的HelloWorld案例写的比较简单&#xff0c;但随着课程渐渐深入&#xff0c;当我们写一些比较难的代码时&#xff0c;在刚开始写完时&#xff0c;你知道这段代码是什么意思&#xff0c;但是等过了几天&#xff0c;再次看这段…
最新文章