Java-进程,线程,多线程,等待唤醒机制(生产者-消费者),线程池

Java-线程

大家好!接下来就由我来带大家学习线程的知识,相信大家也一定可以掌握!
线程相关概念
  • 进程
    • 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。当我们使用迅雷,又启动一个进程,操作系统将为迅雷分配新的内存空间。
    • 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身产生、存在和消亡的过程。
  • 单线程
    • 同一时刻,只允许执行一个线程
  • 多线程
    • 同一时刻,可以执行多个线程,比如:一个QQ进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
  • 并发
    • 同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu 实现的多任务就是并发
  • 并行
    • 同一个时刻,多个任务同时执行,多核cpu可以实现并行

一、什么是线程

  • 线程是由进程创建的,是进程的一个实体
  • 一个进程可以拥有多个线程

二、如何创建线程

  • 继承Thread类,重写run方法

    • public class Thread01 {
          public static void main(String[] args) {
              // 创建Cat对象,可以当作线程使用
              Cat cat = new Cat();
              // 启动线程
              cat.start();
          }
      }
      
      /**
       * 1、当一个类继承了Thread类 ,该类就可以当做线程使用
       * 2、我们会重写run方法 , 写上自己的业务代码
       * 3、run Thread 类 实现了 Runnable 接口的 run方法
       *
       */
      /*
          @Override
          public void run() {
              super.run();
          }
      * */
      class Cat extends Thread{
          int times = 0;
          @Override
          public void run() {
              while (times < 8){
                  times++;
                  // 该线程每隔一秒输出我是小猫咪
                  System.out.println("我是小猫咪"+times);
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
              }
          }
      }
      
  • 实现Runnable接口,重写run方法

    • java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然不可能了
    • java设计者门提供了另外一个方式创建线程,就是通过实现Runnable 接口来创建线程
  • 实现Callable<V>泛型接口,重写call() 方法

    • 需要使用FutureTask类来管理结果,在使用Thread来开启线程

    • 用来获取多线程执行的结果

    • public class Thread05 {
          public static void main(String[] args) throws Exception {
              /*
               * 多线程的第三种实现方式
               *      特点:可以获取到多线程运行的结果
               *      1、创建一个类MyCallable 实现 Callable接口
               *      2、重写call (是有返回值的,表示多线程运行的结果)
               *
               *      3、创建MyCallable对象 (表示多线程要执行的任务)
               *      4、创建FutureTask 的对象 (作用管理多线程运行的结果)
               *      5、创建Thread类的对象,并启动
               */
              MyCallable myThread = new MyCallable();
              FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(myThread);
              Thread thread = new Thread(integerFutureTask);
              thread.start();
              System.out.println(integerFutureTask.get());
      
          }
      }
      
      class MyCallable implements Callable<Integer> {
          @Override
          public Integer call() throws Exception {
              // 实现 1 ~ 100 个数累加
              Integer num = 0;
              for (int i = 0; i <= 100; i++) {
                  num += i;
              }
              return num;
          }
      }
      

三、线程的基本执行流程

  • 调用方法

    public synchronized void start(){
        start0();
    }
    
  • start0() 是本地方法,是JVM调用,底层是 c/c++ 实现

    • 真正实现多线程的效果,是start0() ,而不是run

    • private native void start0();

    • public class Thread02 {
          public static void main(String[] args) {
              Dog dog = new Dog();
      //        dog.start() 不能直接调用
              Thread thread = new Thread(dog);
              thread.start();
          }
      }
      
      class Dog implements Runnable{
          int count = 0;
          @Override
          public void run() {
              while (true){
                  System.out.println("小狗汪汪叫"+(++count)+Thread.currentThread().getName());
                  // 休息1秒
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  if(count > 10){
                      break;
                  }
              }
      
          }
      }
      
    • 用代码模拟实现Runnable 接口

      • public class Thread02 {
            public static void main(String[] args) {
                Tiger tiger = new Tiger();
                ThreadProxy threadProxy = new ThreadProxy(tiger);
                threadProxy.start();
            }
        }
        
        class Animal {}
        class Tiger extends Animal implements Runnable{
        
            @Override
            public void run() {
                System.out.println("老虎嗷嗷嗷");
            }
        }
        
        // 线程代理类,模拟了极简的Thread类
        class ThreadProxy implements Runnable{
        
            private Runnable target = null; //属性Runnable
        
            public ThreadProxy(Runnable target) {
                this.target = target;
            }
        
            @Override
            public void run() {
                if(target != null){
                    target.run();
                }
            }
        
            public void start(){
                start0();
            }
        
            public void start0(){
                run();
            }
        }
        
  • 继承Thread 和实现Runnable的区别

    • java 的设计来看,通过继承Thread 或者实现 Runnable 接口来创建线程本质上没有区别,从官方文档我们可以看到Thread类本身就实现了Runnable 接口
    • 实现Runnable 接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable 接口来实现多线程

四、线程终止

  • 当线程完成任务后,自动退出

  • 还可以通过使用变量来控制run方法退出,主线程控制子线程退出,通知方式,案例:

    public class ThreadExit_ {
        public static void main(String[] args) throws InterruptedException {
            T t = new T();
            t.start();
            // 如果希望main线程去控制t1 线程的终止,必须可以修改 loop
            // 让t 退出run方法,从而终止 t1 线程 -> 通知方法
            // 让主线程休息十秒在让t线程退出
            Thread.sleep(10000);
            t.setLoop(false);
        }
    }
    
    class T extends Thread{
    
        private int count = 0;
    
        private boolean loop = true;
    
        @Override
        public void run() {
            while (loop){
    
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
    
                System.out.println("第"+(++count)+"循环");
            }
        }
    
        public int getCount() {
            return count;
        }
    
        public void setCount(int count) {
            this.count = count;
        }
    
        public boolean isLoop() {
            return loop;
        }
    
        public void setLoop(boolean loop) {
            this.loop = loop;
        }
    }
    

五、Thread类的常用方法

基础方法:
  • Thread.sleep(毫秒) :让线程睡一会儿,暂停一会儿,静态方法

  • 对象.start():开启线程

  • Thread.currentThread() :获取当前线程对象

  • Thread.currentThread().getName():获取当前的线程名

  • setName():设置线程名称,使之与参数name相同

  • run() :调用线程对象的run方法

  • setPriority() : 更改线程的优先级

  • getPriority() : 获取线程的优先级

  • interrupt() : 中断线程

    注意:

    • start 底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新的线程
    • 线程优先级的范围
    • interrrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程

例子:

public class ThreadMethod_ {
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        test.setName("小罗"); //设置名字
        test.setPriority(Thread.MIN_PRIORITY); //设置优先级
        test.start();

        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("主线程 = "+i + "执行优先级="+test.getPriority());
        }

        test.interrupt();//中断线程休眠时间
    }
}

class Test extends Thread{
    @Override
    public void run() {
        while (true){
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " 吃个包子~~");
            }
            try {
                System.out.println(Thread.currentThread().getName() + "休息中~~");
                Thread.sleep(20*1000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "被 interrupt 了");
            }
        }
    }
}
提升方法:
  • yield: 线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功

  • join : 线程的插队。插队的线程一旦插队成功,则肯定线执行完插入的线程所有的任务

  • isAlive():是否处于活动状态,判断线程是否存活,即还没有执行完毕

  • stop():终止线程,已取消,不建议使用。

  • interrupt():中断线程,用于打断阻塞状态的线程,如:sleep、wait、join

    • 如果被中断线程正在sleep,wait,join会导致被中断的线程抛出InterruptedException,并清除中断标记
      • 中断标记(状态)为 false
    • 如果中断正在运行的线程,则会设置中断标记
      • 中断标记(状态)为 true
    • park的线程被中断,也会设置中断标记
  • isInterrupted():判断这个线程是否被中断

    • boolean isInterrupted()

    • 不会清除中断标记

  • interrupted():判断当前线程是否被中断

    • static boolean interrupted()

    • 会清除中断标记

六、用户线程和守护线程

  • 用户线程:也叫做工作线程,当线程的任务执行完或通知方式结束

  • 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

  • 常见的守护线程:垃圾回收机制

  • 创建守护线程案例 myDaemonThread.setDaemon(true);

    注意点:必须要在开启前进行设置

    public class ThreadMethod02 {
        public static void main(String[] args) throws Exception {
            MyDaemonThread myDaemonThread = new MyDaemonThread();
            myDaemonThread.setDaemon(true);
            myDaemonThread.start();
            if (myDaemonThread.isDaemon()) {
                System.out.println("是守护线程");
            }
            for (int i = 0; i < 10; i++) {
                Thread.sleep(1000);
                System.out.println("小叶在工作~~");
            }
        }
    }
    
    class MyDaemonThread extends Thread{
        @Override
        public void run() {
            for (;;){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
    
                System.out.println("小罗和老龚在聊天--哈哈哈哈");
            }
        }
    }
    

七、线程的生命周期

  • NEW :尚未启动的线程

  • RUNNABLE :在Java虚拟机中执行的线程 ,可以细分为

    • Ready : 就绪状态
    • Running :真正在执行的状态
  • BLOCKED:被阻塞等待监视器锁定的线程

  • WAITING:正在等待另一个线程执行特定动作的线程

  • TIMED_WAITING :正在等待另一个线程执行动作达到指定等待时间的线程

  • TERMINATED:已退出的线程处于此状态

线程生命周期图

八、线程同步机制

Synchronized

  • 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。

  • 也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。

  • 案例

    public class HomeWork01 {
        public static void main(String[] args) throws InterruptedException {
            // 题目:使用三个线程分别随机 输出红绿蓝的颜色,RGB格式 0~255之间
            RGB rgb = new RGB();
            RGB rgb1 = new RGB();
            RGB rgb2 = new RGB();
    
            rgb.start();
            rgb1.start();
            rgb2.start();
        }
    }
    
    class RGB extends Thread{
        static int loop = 0;
        String[] rgb = new String[]{
                "红色:","蓝色:","绿色:"
        };
    
        @Override
        public synchronized void run() { //同一时刻只能有一个线程才能进入这个方法
            System.out.println(rgb[loop++]+new Random().nextInt(256));
        }
    }
    

九、线程锁执行原理

1、互斥锁
  • Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性

  • 每个对象都对应与一个可称为 “互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象

  • 关键字synchronized 来与对象的互斥锁联系,当某个对象用synchronized 修饰时表明该对象在任一时刻只能由一个线程访问

  • 同步的局限性:导致程序的执行效率会降低

  • 同步方法(非静态)的锁可以是this,也可以是其它对象(要求是同一个对象)

    • // 当前的方法中的锁就是当前的对象
      public synchronized void m1(){}
      // 
      public void m1 {
          // 这边就需要传入当前的对象
          // 注意:
          //    如果是 new RGB().start(); 
          //    		new RGB().start(); 
          // 这样子开启两个线程的化是锁不住的,推荐下面的方法
          synchronized(this){
              System.out.println("hello");
          }
      }
      // 需要的是同一个对象,不一定就是当前对象,下面这样子也可以完成同步效果
      Object object = new Object();
      public void m1 {
          // 这边就需要传入对象
          synchronized(object){
              System.out.println("hello");
          }
      }
      
  • 同步方法(静态的)的锁为当前类本身

    • /**
       * 静态方法中使用synchronized 中的锁是当前的SellTicket02.class
       */
      public synchronized static void m1(){}
      
      /**
       * 如果要在方法里面加上synchronized 那么里面传入的就是当前类的class
       */
      public synchronized static void m2(){
          synchronized (SellTicket02.class){
              System.out.println("hello");
          }
      }
      
  • 细节以及注意:
    • 如果同步方法如果没有使用static 修饰:默认锁对象为this
    • 如果方法使用static 修饰,默认锁对象:当前类.class
    • 实现的落地步骤:
      • 需要分析上锁的代码
      • 选择同步代码块或同步方法,尽量选择同步代码块,理论上来说同步的代码越少,效率越高
      • 要求多个线程的锁对象为同一个即可
2、线程死锁
  • 解释

    • 多个线程都占用了对象的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生
  • 举例:

    • 妈妈:你先完成作业,才让你玩手机
    • 小明:你先让我玩手机,我才完成作业
  • 案例:

    • public class DeadLock {
          public static void main(String[] args) {
              DeadLockDemo A = new DeadLockDemo(true);
              A.setName("A:");
              DeadLockDemo B = new DeadLockDemo(false);
              B.setName("B:");
              A.start();
              B.start();
              System.out.println(A.getName() +" " + A.getState());
              System.out.println(B.getName()+" " + B.getState());
          }
      }
      
      class DeadLockDemo extends Thread{
          static Object o1 = new Object();
          static Object o2 = new Object();
      
          boolean flag;
          public DeadLockDemo(boolean flag){
              this.flag = flag;
          }
      
          //下面的业务逻辑
          //1、如果flag 为 true,线程A 就会得到/持有 o1 对象锁,然后尝试获取 o2 对象锁
          //2、如果线程A 得不到 o2 对象锁 ,就会Blocked
          //3、如果flag 为 false 线程B 就会得到/持有 o2 对象锁,然后尝试获取 o1 对象锁
          //4、如果线程B 得不到 o1 对象锁 ,就会Blocked
          @Override
          public void run() {
              if(flag){
                  synchronized (o1){ // 对象互斥锁 下面的代码是同步代码
                      System.out.println(Thread.currentThread().getName() + "进入 1");
                      synchronized (o2){
                          System.out.println(Thread.currentThread().getName() + "进入 2");
                      }
                  }
              }else{
                  synchronized (o2){
                      System.out.println(Thread.currentThread().getName() + "进入 3");
                      synchronized (o1){
                          System.out.println(Thread.currentThread().getName() + "进入 4");
                      }
                  }
              }
          }
      }
      
3、Lock锁

使用同步代码块虽然可以解决大部分问题,但是必须限制在synchronized中,并不灵活,JDK5以后提供了一个新的锁对象Lock

  • Lock实现提供比使用synchronized 方法和语句,更加便捷,灵活的锁定操作

  • Lock中提供了获得锁和释放锁的方法

  • 使用Lock 的实现类 ReentrantLock 可以进行操作

  • lock() : 获得锁

  • unlock() :释放锁

  • 案例一:错误示范,某个线程还没还锁就跳出循环导致程序一直在运行

    public class LockTest {
        public static void main(String[] args) {
            MyLock myLock1 = new MyLock();
            MyLock myLock2 = new MyLock();
            MyLock myLock3 = new MyLock();
    
            myLock1.start();
            myLock2.start();
            myLock3.start();
        }
    }
    
    /**
     * 买票
     */
    class MyLock extends Thread{
        static int loop;
        static Lock lock = new ReentrantLock(); // 注意要使用 static 不然锁对象每个人都不一样就会出现问题
    
        @Override
        public void run() {
            while (true){
                lock.lock();// 获得锁
                // 当我们的某个线程卖到第100张票后,就会直接带着锁跑了
                if (loop == 100){
                    // 这里直接break出去了,还没来得及还锁,就会导致程序一直运行
                    break;
                }else{
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
    
                    loop++;
                    System.out.println(getName() + "卖出第"+loop +"张票");
                }
                lock.unlock();// 释放锁
            }
        }
    }
    
  • 案例二:使用finally 语句的特性来修复当前带着锁跑不还锁的代码

    class MyLock01 extends Thread{
        static int loop;
        static Lock lock = new ReentrantLock(); // 注意要使用 static 不然锁对象每个人都不一样就会出现问题
    
        @Override
        public void run() {
            while (true){
                try {
                    lock.lock();// 获得锁
                    // 当我们的某个线程卖到第100张票后,就会直接带着锁跑了
                    if (loop == 100){
                        // 这里直接break出去了,还没来得及还锁,就会导致程序一直运行
                        break;
                    }else{
                        Thread.sleep(10);
                        loop++;
                        System.out.println(getName() + "卖出第"+loop +"张票");
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }finally { // 使用finally语句的特性来优化释放锁
                    lock.unlock();// 释放锁
                }
            }
        }
    }
    
4、释放锁
1、会释放锁
  • 当前线程的同步方法、同步代码块执行结束
  • 当前线程在同步代码块、同步方法中遇到breakretutn.
  • 当前线程在同步代码块、同步方法中出现了未处理的ErrorException,导致异常结束
  • 当前线程在同步代码块、同步方法中执行了线程对象的wait() 方法,当前线程暂停,并释放锁。
2、不会释放锁
  • 线程执行同步代码块或同步方法时,程序调用Tread.sleep()Threa.yield()方法暂停当前线程的执行,不会释放锁。
  • 线程执行同步代码块时,其它线程调用了该线程的suspend() 方法将该线程挂起,该线程不会释放锁。

十、等待唤醒机制

作用:在多线程编程中,等待唤醒机制(Wait-Notify Mechanism)是一种重要的同步工具,主要用于解决线程之间的协作问题。

  • wait()

    • 使当前线程进入等待状态,并释放当前对象的锁,当其他线程调用该对象的notify()notifyAll()方法时,当前线程可能会被唤醒
    • 如果没有其他线程调用notify()notifyAll()方法,调用wait()的线程将一直等待。
  • notify()

    • 唤醒一个正在等待当前对象锁的线程。如果有多个线程正在等待,JVM会随机选择一个线程进行唤醒。
    • 被唤醒的线程不会立即恢复执行,它必须等待当前线程释放对象锁后才能继续执行。
  • notifyAll()

    • 方法唤醒所有正在等待当前对象锁的线程。与notify()不同的是,notifyAll()会唤醒所有等待的线程,而notify()只会唤醒一个。
    • 被唤醒的线程同样需要等待当前线程释放对象锁后才能继续执行。
  • 生产者-消费者问题:这是等待唤醒机制最常见的应用场景之一。生产者线程负责生成数据并将其放入缓冲区,消费者线程负责从缓冲区中取出数据进行处理。当缓冲区为空时,消费者线程需要等待,直到生产者线程放入数据;当缓冲区已满时,生产者线程需要等待,直到消费者线程取出数据。通过等待唤醒机制,可以高效地协调生产者和消费者的行为。

  • 案例:

    import java.util.LinkedList;
    import java.util.Queue;
    
    public class ProducerConsumerExample {
        private final Queue<Integer> queue = new LinkedList<>();
        private final int capacity;
    
        public ProducerConsumerExample(int capacity) {
            this.capacity = capacity;
        }
    
        // 生产者方法
        public synchronized void put(int value) throws InterruptedException {
            while (queue.size() == capacity) {
                wait(); // 等待,直到缓冲区有空间
            }
            queue.add(value);
            notifyAll(); // 唤醒等待的消费者线程
            System.out.println("Produced: " + value + ", Queue size: " + queue.size());
        }
    
        // 消费者方法
        public synchronized int get() throws InterruptedException {
            while (queue.isEmpty()) {
                wait(); // 等待,直到缓冲区有数据
            }
            int value = queue.poll();
            notifyAll(); // 唤醒等待的生产者线程
            System.out.println("Consumed: " + value + ", Queue size: " + queue.size());
            return value;
        }
    
        public static void main(String[] args) {
            ProducerConsumerExample example = new ProducerConsumerExample(2);
    
            // 生产者线程
            Thread producerThread = new Thread(() -> {
                try {
                    for (int i = 0; i < 10; i++) {
                        example.put(i);
                        Thread.sleep((int) (Math.random() * 1000));
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            // 消费者线程
            Thread consumerThread = new Thread(() -> {
                try {
                    for (int i = 0; i < 10; i++) {
                        example.get();
                        Thread.sleep((int) (Math.random() * 1000));
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            producerThread.start();
            consumerThread.start();
        }
    }
    

十一、线程池

  • 常用线程池:
线程池类型特点适用场景
FixedThreadPool线程池大小固定,可以有效控制线程的最大并发数任务量可以预估并且线程数量不需要频繁变化的场景,如订单处理系统
CachedThreadPool线程池大小动态调整,空闲线程会被回收执行大量短期小任务的场景,如高并发的Web应用
SingleThreadExecutor线程池中只有一个线程,保证任务顺序执行需要保证任务顺序执行的场景,如日志记录系统
ScheduledThreadPool支持定时任务和周期性任务定时任务调度,如定时清理数据库中的临时数据、定时发送邮件通知
  • Java线程池的常用方法:
方法名称描述示例代码
ExecutorService newFixedThreadPool(int nThreads)创建一个固定大小的线程池ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
ExecutorService newCachedThreadPool()创建一个可缓存线程池ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ExecutorService newSingleThreadExecutor()创建一个单线程化的线程池ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个支持定时及周期性任务执行的线程池ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
execute(Runnable command)提交一个不需要返回值的任务到线程池中执行fixedThreadPool.execute(() -> System.out.println("执行任务"));
submit(Runnable task)提交一个不需要返回值的任务到线程池中执行,返回Future<?>Future<?> future = fixedThreadPool.submit(() -> System.out.println("执行任务"));
submit(Callable<T> task)提交一个有返回值的任务到线程池中执行,返回Future<T>Future<String> future = fixedThreadPool.submit(() -> "任务返回值");
shutdown()尝试平滑地关闭线程池,等待已提交任务完成fixedThreadPool.shutdown();
shutdownNow()尝试立即关闭线程池,中断正在执行的任务List<Runnable> notExecutedTasks = fixedThreadPool.shutdownNow();
  • 案例:
public class MyThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        // 使用线程池
        executorService.submit(new ThreadTest());
        executorService.submit(new ThreadTest());
        executorService.submit(new ThreadTest());

    }
}
class ThreadTest implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+" : 你好!!"+i);
        }
    }
}
  • 自定义线程池

    • 参数一:核心线程数量 不能小于0
    • 参数二:最大线程数 不能小于等于0,最大数量 >= 核心线程数量
    • 参数三:空闲线程最大存活时间 不能小于0
    • 参数四:时间单位
      • 使用TimeUnit指定
    • 参数五:任务队列
      • 不能为null
      • 有两种阻塞队列,一种是有长度的,一种是没有长度的
      • new ArrayBlockingQueue<>(3) : 有长度的
      • new LinkedBlockingDeque<>() : 没有长度的
    • 参数六:创建线程工厂
      • 不能为null
      • 需要调用Executors里面的方法
    • 参数七:任务的拒绝策略,一共有四种
      • 不能为null
      • new ThreadPoolExecutor.AbortPolicy() :默认策略:丢弃任务并抛出 RejectedExecutionException 异常
      • 丢弃任务,但是不抛出异常,这是不推荐的做法
      • 抛弃队列中等待最久的任务,然后把当前任务加入队列中
      • 调用任务的 run() 方法绕过线程池直接执行
    public class CustomizeThreadPoolDemo {
        public static void main(String[] args) {
            /*
                ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
                (核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
    
                参数一:核心线程数量	               不能小于0
                参数二:最大线程数                   不能小于等于0,最大数量 >= 核心线程数量
                参数三:空闲线程最大存活时间           不能小于0
                参数四:时间单位                     使用TimeUnit指定
                参数五:任务队列                     不能为null
                参数六:创建线程工厂                  不能为null
                参数七:任务的拒绝策略                不能为null
             */
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    3,
                    6,
                    60,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy()
            );
    
            threadPoolExecutor.submit(new ThreadTest());
            threadPoolExecutor.submit(new ThreadTest());
        }
    }
    

略);

          参数一:核心线程数量	               不能小于0
          参数二:最大线程数                   不能小于等于0,最大数量 >= 核心线程数量
          参数三:空闲线程最大存活时间           不能小于0
          参数四:时间单位                     使用TimeUnit指定
          参数五:任务队列                     不能为null
          参数六:创建线程工厂                  不能为null
          参数七:任务的拒绝策略                不能为null
       */
      ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
              3,
              6,
              60,
              TimeUnit.SECONDS,
              new ArrayBlockingQueue<>(3),
              Executors.defaultThreadFactory(),
              new ThreadPoolExecutor.AbortPolicy()
      );

      threadPoolExecutor.submit(new ThreadTest());
      threadPoolExecutor.submit(new ThreadTest());
  }

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值