JUC多线程个人笔记

1.基础知识

并发编程的目的是为了让程序变得更快,但是,并不是启动更多的线程就能让程序最大限度的并发执行。在进行并发编程中,如果希望通过多线程让程序运行的更快,会面临非常多的挑战。比如上下文切换、死锁问题,以及受限于硬件和软件的资源问题。

多线程最主要的作用是压榨cpu.

1.1死锁

死锁就是两个进程(线程)同时占用两个资源,但又在彼此等待对方释放锁,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁

代码演示:

java 复制代码
public class DeadLock {

    private static String Resource_A = "A";
    private static String Resource_B = "B";


    public static void main(String[] args) {

        new DeadLock().deadLock();

    }
    private void  deadLock(){
        Thread t1 = new Thread(new Runnable() {
            public void run() {
               synchronized (Resource_A){
                   try {
                       Thread.sleep(3000);//睡眠3s的意思 让B线程启动起来 去使用Resource_B资源
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   synchronized (Resource_B){
                       System.out.println("1");
                   }
               }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                 synchronized (Resource_B){
                     synchronized (Resource_A){
                         System.out.println("2");
                     }
                 }
            }
        });
        try {
            t1.start();
            Thread.sleep(2000);//睡眠2s的意思 让线程1先启动
            t2.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

1.2并行和并发

并发:一个处理器同一时间段处理多个任务,强调一个时间段。(不是真正的同时,而是看起来同时,逻辑上的同时发生)

并行:多个处理器同一时刻处理多个任务。真正的同时。(物理意义上的同时发生)

2.线程的创建

2.1继承Thread类

java 复制代码
public class MyThread extends Thread {
    @Override
    public void run() {
        for(int i=0; i<100; i++) {
            System.out.println(i);
        }
    }
}
public class MyThreadDemo {
    public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();
        my1.start();
        my2.start();
    }
}

run()是用来封装被线程执行的代码。start()启动线程;然后由JVM调用此线程的run()方法

2.2 实现Runnable接口

代码实现1:

java 复制代码
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0; i<100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public class MyRunnableDemo {
    public static void main(String[] args) {
        //创建MyRunnable类的对象
        MyRunnable my = new MyRunnable();
        //创建Thread类的对象,把MyRunnable对象作为构造方法的参数
        Thread t1 = new Thread(my,"坦克");
        Thread t2 = new Thread(my,"飞机");

        //启动线程
        t1.start();
        t2.start();
    }
}

匿名内部类实现:

java 复制代码
new Thread(new Runnable() {
    @Override
    public void run() {
         // 调用资源方法,完成业务逻辑
    }
}, "your thread name").start();

2.3实现Callable接口

java 复制代码
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("跟女孩表白" + i);
        }
        //返回值就表示线程运行完毕之后的结果
        return "答应";
    }
}
public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //线程开启之后需要执行里面的call方法
        MyCallable mc = new MyCallable();

        //Thread t1 = new Thread(mc);

        //可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
        FutureTask<String> ft = new FutureTask<>(mc);

        //创建线程对象
        Thread t1 = new Thread(ft);

        String s = ft.get();
        //开启线程
        t1.start();

        //String s = ft.get();
        System.out.println(s);
    }
}

2.4线程睡眠

java 复制代码
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        /*System.out.println("睡觉前");
        Thread.sleep(3000);
        System.out.println("睡醒了");*/

        MyRunnable mr = new MyRunnable();

        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        t1.start();
        t2.start();
    }
}

3.线程周期

java 复制代码
public class Thread{
    public enum State{

      NEW,
      RUNNABLE,
      BLOCKED,
      WAITING,
      TIME_WATING,
      TERMINATED 

    }
}

4.线程同步

4.1Synchronized

synchronized 同步块是 Java 提供的一种原子性内置锁,Java 中的每个对象都可以把它当作一个同步锁来使用,这些 Java 内置的使用者看不到的锁被称为内置锁,也叫作监视器锁。

每个对象Object都内置了一个监视器锁,当某个线程获得了这个监视器锁后,其它线程在想获得这个对象的监视器锁,就必须要排队。也就是说:synchronzied关键字的底层,相当于一个排它锁。

java 复制代码
public class SellTicket implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
                //t1进来后,就会把这段代码给锁起来
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                        //t1休息100毫秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //窗口1正在出售第100张票
                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                    tickets--; //tickets = 99;
                }
            }
            //t1出来了,这段代码的锁就被释放了
        }
    }
}

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();

        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

4.2Lock

在功能上等同于synchronized.

能用synchronized都能用Lock,能用Lockd的不一定都能用synchronized.

不管是synchronized还是Lock,这俩都属于本地锁。

本地锁:锁对象都是jvm虚拟机中的对象。【只要一个对象自己能new 出现,这个对象都叫本地对象】

分布式锁:锁对象不是jvm虚拟机中的对象(属于自己组件中的对象)这个对象从来没new 过,组件给你new 的 你直接使用。

Lock是一个接口,这里主要有三个实现:ReentrantLock、ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.WriteLock

4.2.1ReentrantLock
java 复制代码
class Tickets_A {
     private int number = 10;
    //创建lock实现类
     final Lock lock = new ReentrantLock();
     public void saleTickets() {
        System.out.println(Thread.currentThread().getName() +"线程进来了,现有票" + number + "张:并且开始卖票,");
        lock.lock();
        try {
            number--;
            System.out.println(Thread.currentThread().getName() + "线程抢到了锁,并卖了一张票,还剩下" + number + "张票");
            Thread.sleep(1000);
            if (number <= 0) {
                System.out.println("票已售罄!");
                return;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}
    public class MultipleSaleTicketsByLock {
    public static void main(String[] args) {
        //资源对象
        Tickets_A tickets_a = new Tickets_A();
        for (int i = 1; i <= 2; i++) {
            new Thread(() -> {
                for (int j = 0; j <5 ; j++) {
                    tickets_a.saleTickets();
                }
            }, i + "").start();
        }
    }

}

可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动拥有该锁(不用抢了)。Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁

java 复制代码
class A{
    public synchronized void aa(){
        ......
       this.bb();
        ......
    }
    public synchronized void bb(){
        ......
    }
}
A a = new A();
a.aa();

A类中有两个普通同步方法,都需要对象a的锁。如果是不可重入锁的话,aa方法首先获取到锁,aa方法在执行的过程中需要调用bb方法,此时锁被aa方法占有,bb方法无法获取到锁,这样就会导致bb方法无法执行,aa方法也无法执行完,出现了死锁情况。可重入锁可避免这种死锁的发生。

ReentrantLock还可以实现公平锁。所谓公平锁,也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。

4.2.2ReentrantLock和synchronized区别

独占锁:在任意一时刻,有且只有一个线程能够获取锁

  1. lock是一个接口,而synchronized是java的一个关键字
  2. Lock位于juc.locks包中,synchronizedjdk内置的锁。
  3. synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
  4. synchronized 可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。可以避免死锁。
  5. synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁,而ReentrantLock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
  6. Synchronized不可响应中断:第一个线程获得某把锁后,第二个线程也想要获得该锁,则它必须处于阻塞或等待状态。如果第一个线程不释放锁,那第二个线程就会一直阻塞或等待,不可被中断。

5.线程通信


案例生产者消费者

所谓生产者消费者问题,实际上主要是包含了两类线程:

​ 一类是生产者线程用于生产数据

​ 一类是消费者线程用于消费数据

为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库

生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为

消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

java 复制代码
public class Desk {

    //定义一个标记
    //true 就表示桌子上有汉堡包的,此时允许吃货执行
    //false 就表示桌子上没有汉堡包的,此时允许厨师执行
    //public static boolean flag = false;
    private boolean flag;

    //汉堡包的总数量
    //public static int count = 10;
    //以后我们在使用这种必须有默认值的变量
   // private int count = 10;
    private int count;

    //锁对象
    //public static final Object lock = new Object();
    private final Object lock = new Object();

    public Desk() {
        this(false,10); // 在空参内部调用带参,对成员变量进行赋值,之后就可以直接使用成员变量了
    }

    public Desk(boolean flag, int count) {
        this.flag = flag;
        this.count = count;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public Object getLock() {
        return lock;
    }

    @Override
    public String toString() {
        return "Desk{" +
                "flag=" + flag +
                ", count=" + count +
                ", lock=" + lock +
                '}';
    }
}

public class Cooker extends Thread {

    private Desk desk;

    public Cooker(Desk desk) {
        this.desk = desk;
    }
//    生产者步骤:
//            1,判断桌子上是否有汉堡包
//    如果有就等待,如果没有才生产。
//            2,把汉堡包放在桌子上。
//            3,叫醒等待的消费者开吃。

    @Override
    public void run() {
        while(true){
            synchronized (desk.getLock()){
                if(desk.getCount() == 0){
                    break;
                }else{
                    //System.out.println("验证一下是否执行了");
                    if(!desk.isFlag()){
                        //生产
                        System.out.println("厨师正在生产汉堡包");
                        desk.setFlag(true);
                        desk.getLock().notifyAll();
                    }else{
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

public class Foodie extends Thread {
    private Desk desk;

    public Foodie(Desk desk) {
        this.desk = desk;
    }

    @Override
    public void run() {
//        1,判断桌子上是否有汉堡包。
//        2,如果没有就等待。
//        3,如果有就开吃
//        4,吃完之后,桌子上的汉堡包就没有了
//                叫醒等待的生产者继续生产
//        汉堡包的总数量减一

        //套路:
            //1. while(true)死循环
            //2. synchronized 锁,锁对象要唯一
            //3. 判断,共享数据是否结束. 结束
            //4. 判断,共享数据是否结束. 没有结束
        while(true){
            synchronized (desk.getLock()){
                if(desk.getCount() == 0){
                    break;
                }else{
                    //System.out.println("验证一下是否执行了");
                    if(desk.isFlag()){
                        //有
                        System.out.println("吃货在吃汉堡包");
                        desk.setFlag(false);
                        desk.getLock().notifyAll();
                        desk.setCount(desk.getCount() - 1);
                    }else{
                        //没有就等待
                        //使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法.
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

    }
}

public class Demo {
    public static void main(String[] args) {
        /*消费者步骤:
        1,判断桌子上是否有汉堡包。
        2,如果没有就等待。
        3,如果有就开吃
        4,吃完之后,桌子上的汉堡包就没有了
                叫醒等待的生产者继续生产
        汉堡包的总数量减一*/

        /*生产者步骤:
        1,判断桌子上是否有汉堡包
        如果有就等待,如果没有才生产。
        2,把汉堡包放在桌子上。
        3,叫醒等待的消费者开吃。*/

        Desk desk = new Desk();

        Foodie f = new Foodie(desk);
        Cooker c = new Cooker(desk);

        f.start();
        c.start();

    }
}

6.并发工具类

6.1 List中的线程安全

这段是线程不安全的

java 复制代码
public class NoSafeList {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i <1000; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

可以使用Vector和SynchronizedList保证线程安全

java 复制代码
public class NoSafeList {

    public static void main(String[] args) {
      //  ArrayList<String> list = new ArrayList<>();
         List<Object> list = Collections.synchronizedList(new ArrayList<>());
         // Vector list = new Vector<Integer>();
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0,2));
                System.out.println(list);
            }).start();

        }

    }
}

6.2 ConcurrentHashMap

在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。

1 ,HashMap是线程不安全的。多线程环境下会有数据安全问题

​ 2 ,Hashtable是线程安全的,但是会将整张表锁起来,效率低下

​ 3,ConcurrentHashMap也是线程安全的,效率较高。

java 复制代码
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;

public class MyConcurrentHashMapDemo {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap<String, String> hm = new ConcurrentHashMap<>(100);

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 25; i++) {
                hm.put(i + "", i + "");
            }
        });


        Thread t2 = new Thread(() -> {
            for (int i = 25; i < 51; i++) {
                hm.put(i + "", i + "");
            }
        });

        t1.start();
        t2.start();

        System.out.println("----------------------------");
        //为了t1和t2能把数据全部添加完毕
        Thread.sleep(1000);

        //0-0 1-1 ..... 50- 50

        for (int i = 0; i < 51; i++) {
            System.out.println(hm.get(i + ""));
        }//0 1 2 3 .... 50
    }
}

6.3CountDownLatch

java 复制代码
new CountDownLatch(int count) //实例化一个倒计数器,count指定初始计数
countDown() // 每调用一次,计数减一
await() //等待,当计数减到0时,阻塞线程(可以是一个,也可以是多个)并行执行

案例:

java 复制代码
class Player implements Runnable {

    private String playerName;

    private CountDownLatch countDownLatch;

    public Player(String playerName, CountDownLatch countDownLatch) {
        this.playerName = playerName;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        System.out.println(playerName + "开始准备");
        try {
            Thread.sleep(2000);
            System.out.println(playerName + "准备完成");
            countDownLatch.countDown();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class CountDownLatchDemo {


    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);//指定一个初始计数
        new Thread(new Player("Player-1", countDownLatch)).start();
        new Thread(new Player("Player-2", countDownLatch)).start();
        //
        countDownLatch.await();
        System.out.println("游戏开始");

    }

}

上述代码join也能实现

java 复制代码
class Player1 implements Runnable {

    private String playerName;

    public Player1(String playerName) {
        this.playerName = playerName;
    }
    @Override
    public void run() {
        try {
            System.out.println(playerName + "开始准备");
            Thread.sleep(2000);
            System.out.println(playerName+"准备完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class JoinDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread player1 = new Thread(new Player1("player-1"));
        Thread player2 = new Thread(new Player1("player-2"));
        player1.start();
        player2.start();
        player1.join();
        player2.join();
        System.out.println("游戏开始");
    }

}

调用一个子线程的 join()方法后,子线程程会一直被阻塞直到该线程运行完毕。而 CountDownLatch 则使用计数器允许子线程运行完毕或者运行中时候递减计数,也就是 CountDownLatch 可以在子线程运行任何时候让 await 方法返回而不一定必须等到线程结束,countDownLatch 相比 Join 方法让我们对线程同步有更灵活的控制。

6.4CyclicBarrier

这个类的中文意思是"循环栅栏"。大概的意思就是一个可循环利用的屏障。该命令只在每个屏障点运行一次。若在所有参与线程之前更新共享状态,此屏障操作很有用
常用方法:

  1. CyclicBarrier(int parties, Runnable barrierAction) 创建一个CyclicBarrier实例,parties指定参与相互等待的线程数,barrierAction一个可选的Runnable命令,该命令只在每个屏障点运行一次,可以在执行后续业务之前共享状态。该操作由最后一个进入屏障点的线程执行。
  2. CyclicBarrier(int parties) 创建一个CyclicBarrier实例,parties指定参与相互等待的线程数。
  3. await() 该方法被调用时表示当前线程已经到达屏障点,当前线程阻塞进入休眠状态,直到所有线程都到达屏障点,当前线程才会被唤醒。

案例:

java 复制代码
class CyclicBarrierResource implements Runnable {


    String studentName;

    CyclicBarrier cyclicBarrier;

    public CyclicBarrierResource(String studentName, CyclicBarrier cyclicBarrier) {
        this.studentName = studentName;
        this.cyclicBarrier = cyclicBarrier;
    }


    @Override
    public void run() {
        //模拟每个人到达教室的时间
        try {

            TimeUnit.SECONDS.sleep((long) Math.random() * 5000);
            System.out.println(studentName + "到达教室门口");
            cyclicBarrier.await();//等待其它人到达教室门口
            System.out.println(studentName+"进入教室");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
            System.out.println("最后一个达到教室,罚款200元");
        });
      new Thread(new CyclicBarrierResource("first student",cyclicBarrier)).start();
      new Thread(new CyclicBarrierResource("tow student",cyclicBarrier)).start();
      new Thread(new CyclicBarrierResource("three student",cyclicBarrier)).start();


    }

}

6.5Semaphore

emaphore可以控制同时访问的线程个数。非常适合需求量大,而资源又很紧张的情况。比如给定一个资源数目有限的资源池,假设资源数目为N,每一个线程均可获取一个资源,但是当资源分配完毕时,后来线程需要阻塞等待,直到前面已持有资源的线程释放资源之后才能继续。

Semaphore:主要的场景就是限流:但是只能对单个微服务限流,如果是某个微服务的集群,那个Juc下的Semaphore就做不到。就需要找个分布式的限流工具。
Redisson----也有一个信号量Semaphore。----对整个集群来完成限流。
Juc-----信号量Semaphore----对单个实例完成限流,不能够对整个集群起到限流。
Sentinel:组件---给程序员用。

public Semaphore(int permits) // 构造方法,permits指资源数目(信号量)

public void acquire() throws InterruptedException // 占用资源,当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。

public void release() // (释放)实际上会将信号量的值加1,然后唤醒等待的线程。

信号量主要用于两个目的:

  • 多个共享资源的互斥使用。
  • 用于并发线程数的控制。保护一个关键部分不要一次输入超过N个线程。

7.阻塞队列


方法:

代码演示:

java 复制代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class BlockQueueDemo {

    public static void main(String[] args) {
        ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<String>(5);
        // 一组add  remove
        //addRemove(blockingQueue);
        
        // 二组:offer poll
        // offerPoll(blockingQueue);
        
        // 三组:put take
        //  putTake(blockingQueue);
        
        // 四组:带时间 
       // offerPollWithTime(blockingQueue);

    }

    private static void offerPollWithTime(ArrayBlockingQueue<String> blockingQueue) {
        try {
            System.out.println(blockingQueue.offer("a"));
            System.out.println(blockingQueue.offer("b"));
            System.out.println(blockingQueue.offer("c"));
            System.out.println(blockingQueue.offer("d"));
            System.out.println(blockingQueue.offer("e"));
            try {
                System.out.println(blockingQueue.offer("f",5, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String poll = blockingQueue.poll(3, TimeUnit.SECONDS);
            System.out.println(poll);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void putTake(ArrayBlockingQueue<String> blockingQueue) {

        for (int i = 1; i <=5; i++) {
            try {
                blockingQueue.put(""+i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        new Thread(()->{
            try {
                Thread.sleep(5000);
                blockingQueue.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        try {
            System.out.println(Thread.currentThread().getName()+"开始放入6");
            blockingQueue.put("6");
            System.out.println(Thread.currentThread().getName()+"放入6成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void offerPoll(ArrayBlockingQueue<String> blockingQueue) {
        for (int i = 1; i <=5; i++) {
            boolean flag = blockingQueue.offer("" + i);
            System.out.println(flag);
           //  System.out.println(blockingQueue.poll());
        }
        boolean offer = blockingQueue.offer("6");
        System.out.println(offer);
        String poll = blockingQueue.poll();
        System.out.println(poll);
    }

    private static void addRemove(ArrayBlockingQueue<String> blockingQueue) {
        for (int i = 1; i <=5; i++) {
            boolean flag = blockingQueue.add("" + i);
            System.out.println(flag);
//            System.out.println(blockingQueue.remove());
        }
        boolean add = blockingQueue.add("6");
        System.out.println(add);
        System.out.println(blockingQueue.remove());
    }
}

8.线程池

线程池是一种池化的技术,类似的还有数据库连接池、HTTP 连接池等等。

池化的思想主要是为了减少每次获取和结束资源的消耗,提高对资源的利用率。
线程池的优势:线程复用;控制最大并发数;管理线程。

8.1Executors工具类

代码:

java 复制代码
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
        //   一池一线程
        //  singleThreadPool();
        //  一池多线程
        //  FixedThreadPool();
        //   创建缓存的线程
        // CacheThreadPool();

        // 创建带延时任务或者定时任务的线程池


        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);


//        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
//        String format = simpleDateFormat.format(new Date());
//        System.out.println("开始准备执行任务时间为:"+format);
//        scheduledExecutorService.schedule(()->{
//            String format1 = simpleDateFormat.format(new Date());
//            System.out.println();
//            System.out.println("执行任务---do  some thing--时间为"+format1);
//        },5, TimeUnit.SECONDS);
//        scheduledExecutorService.shutdown();


//        ScheduledFuture<String> schedule = scheduledExecutorService.schedule(new Callable<String>() {
//            @Override
//            public String call() throws Exception {
//                return Thread.currentThread().getName();
//            }
//        }, 5, TimeUnit.SECONDS);
//
//        String s = null;
//        try {
//            s = schedule.get();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } catch (ExecutionException e) {
//            e.printStackTrace();
//        }
//        System.out.println("结果---" + s);



//        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd Hh:mm:ss");
//        System.out.println("当前时间"+simpleDateFormat.format(new Date()));
//        scheduledExecutorService.scheduleAtFixedRate(()->{
//            String format = simpleDateFormat.format(new Date());
//            System.out.println(format+"开始执行任务do some thing");
//            try {
//                Thread.sleep(6000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        },3,5,TimeUnit.SECONDS);

        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yy-MM-dd Hh:mm:ss");
        System.out.println("当前时间"+simpleDateFormat.format(new Date()));
        scheduledExecutorService.scheduleWithFixedDelay(()->{
            String format = simpleDateFormat.format(new Date());
            System.out.println(format+"开始执行任务do some thing");
            try {
                Thread.sleep(6000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },3,5,TimeUnit.SECONDS);
    }

    private static void CacheThreadPool() {
        ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {
            int i = 0;

            @Override
            public Thread newThread(Runnable r) {
                i++;
                Thread thread = new Thread(r);
                thread.setName("myPool-" + i + "-thread-" + i);
                return thread;
            }
        });

        for (int i = 1; i <= 10; i++) {
            int finalI = i;
            executorService.execute(() -> {
                System.out.println("任务" + finalI + "被线程池中的线程" + Thread.currentThread().getName() + "执行");
            });
        }
    }

    private static void FixedThreadPool() {
        ExecutorService executorService = Executors.newFixedThreadPool(5, new ThreadFactory() {
            int i = 0;

            @Override
            public Thread newThread(Runnable r) {
                i++;
                Thread thread = new Thread(r);
                thread.setName("myPool-" + i + "-thread-" + i);
                return thread;
            }
        });
        for (int i = 1; i <= 8; i++) {
            int finalI = i;
            executorService.execute(() -> {

                System.out.println("执行的任务" + finalI + "被线程池中线程" + Thread.currentThread().getName() + "执行");
            });
        }	

        executorService.shutdown();
    }

    private static void singleThreadPool() {
        //1。一池一线程
        ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
            int i = 0;

            @Override
            public Thread newThread(Runnable r) {
                i++;
                Thread thread = new Thread(r);
                thread.setName("myPool-" + i + "thread-" + i);
                return thread;
            }
        });
        executorService.execute(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("执行任务所用的线程为:" + Thread.currentThread().getName());
            }
        });
        //  executorService.shutdown();
    }
}

8.2ThreadPoolExecutor(常用)

8.2.1使用
java 复制代码
public class ThreadPoolDemo {
    public static void main(String[] args) {
 
        ExecutorService threadPool = new ThreadPoolExecutor(2, 5,
                2, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println("自定义拒绝策略");
                    }
                }
        );

        try {
            for (int i = 0; i < 2; i++) {
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "执行了业务逻辑");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}
8.2.2 参数
8.2.3 底层工作原理


重要:

1.在创建了线程池后,线程池中的线程数为零。

2.当调用execute()方法添加一个请求任务时,线程池会做出如下判断:

如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;

如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;

如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。

3.当一个线程完成任务时,它会从队列中取下一个任务来执行。

4.当一个线程无事可做超过一定的时间(keepAliveTime)时,线程会判断:

如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。

所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小

8.2.3.1拒绝策略

9其他

9.1 java内存模型(JMM)

9.1 volatile

9.2 cas

相关推荐
yfs10244 小时前
钉钉补卡事件处理方案
java·数据库·钉钉
大飞哥~BigFei4 小时前
BigDecimal账户分布式原子操作
java·分布式
fatiaozhang95276 小时前
创维LB2004_安装软件教程
android·网络·电视盒子·刷机固件·机顶盒刷机
编啊编程啊程6 小时前
响应式编程框架Reactor【4】
java·开发语言
来来走走12 小时前
Flutter MVVM+provider的基本示例
android·flutter
Funcy13 小时前
XxlJob源码分析01:环境准备
java
不会学习?13 小时前
数据结构 第三轮
数据结构·经验分享·笔记·其他
the beard13 小时前
Feign整合Sentinel实现服务降级与Feign拦截器实战指南
java·spring·sentinel
Pu_Nine_913 小时前
Axios 实例配置指南
前端·笔记·typescript·axios