文章目录
多线程的创建
方式1:继承Thread类的方式进行实现
package day30_多线程; public class Demo01 { // 方式1:继承Thread类的方式进行实现 // 步骤1:继承Thread类 // 步骤2:重写run方法 // 步骤3:创建MyThread的对象,调用start方法启动线程 public static void main(String[] args) { MyThread t1 = new MyThread(); t1.setName("线程1");// 给线程设置名字 // 开启线程--此处需要注意:不能直接调用run方法,否则就是对象的创建和方法的调用 t1.start(); t1.run(); for (int i = 0; i <= 50; i++) { System.out.println("main" + i); } } }
package day30_多线程; public class MyThread extends Thread { // 重写run方法,线程要执行的代码 @Override public void run() { for (int i = 0; i <= 50; i++) { // setName("测试"); System.out.println(getName() + ":" + i); } } }
方式2:实现Runnable接口的方式进行实现
package day30_多线程; public class Demo02 { // 方式2:实现Runnable接口的方式进行实现 // 1.自定义一个类实现Runnable接口 // 2.重写run方法 // 3.创建自定义类的对象,将其作为参数 // 4.创建一个Thread对象,参数为3,并开启线程 public static void main(String[] args) { // 创建MyRunable的对象,表示多线程要执行的任务 MyRunable r1 = new MyRunable(); // 创建线程对象 Thread t1 = new Thread(r1); Thread t2 = new Thread(r1); t1.setName("子线程1"); t2.setName("子线程2"); t1.start();// 开启线程 t2.start(); // 设置主线程的名字 Thread mainThread = Thread.currentThread(); mainThread.setName("main主线程"); for (int i = 0; i <= 50; i++) { System.out.println(mainThread.getName() + ":" + i); } } }
package day30_多线程; public class MyRunable implements Runnable { @Override public void run() { for (int i = 0; i <= 50; i++) { // getName()为Thread类的方法,不能直接使用getName()方法 // MyRunable和Thread类没有继承关系 // 通过Thread的静态方法获取当前线程对象后获取方法名字 Thread t1 = Thread.currentThread(); System.out.println(t1.getName() + ":" + i); } } }
方式3:实现Callable接口的方式进行实现线程
package day30_多线程; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Demo03 { public static void main(String[] args) throws InterruptedException, ExecutionException { // 方式3:实现Callable接口的方式进行实现线程---和方式2相同 // 特点:可以获取到多线程的运行结果 // 步骤1:创建一个实现类来实现Callable接口 // 步骤2:重写ca11方法(有返回值,表示多线程执行的任务的返回值) // 步骤3:创建实现类对象 // 步骤4:创建FutureTask类型的对象(管理多线程执行后的结果) // 步骤5:Thread类的对象的创建,并启动 // 创建Callable实现类对象(表示多线程要执行的任务) MyCallable m1 = new MyCallable(); // 创建FutureTask对象(作用管理多线程的运行结果) FutureTask<Integer> ft = new FutureTask<Integer>(m1); // 创建线程对象 Thread t1 = new Thread(ft); // 启动线程 t1.start(); // 获取多线程运行的结果 Integer num = ft.get(); System.out.println(num); } }
package day30_多线程; import java.util.concurrent.Callable; public class MyCallable implements Callable<Integer> { //线程要执行的代码 @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i <= 100; i++) { sum += i; } return sum; } }
多线程的方法
**getName()😗*获取此线程名字
细节:如果没有给线程设置名字.线程也会有默认名称
格式:Thread-X(X的序号从0开始)
**void setName(String name)**设置线程的名字,构造方法进行设置线程名称
**Thread.currentThread();**获取当前的线程
**Thread.sleep(XXX);**让指定的线程休眠多少毫秒
细节:哪条线程执行到此方法,那么哪条线程就会在这里停留对应的时间
参数为毫秒 1秒=1000毫秒
当时间到了以后,线程会自动的醒来,继续执行下面的代码
getPriority() 获取优先级
setPriority() 设置优先级,范围1~10,默认优先级为5
数字越大优先级越高,但不是一定先执行完
**setDaemon(true)**设置守护线程
Thread.yield()
让出当前的cpu的执行权,让结果更加均匀一点,也不是绝对均匀。
当前的线程让出执行权后,再次进行抢夺资源也可能会抢到。
join()线程插队
守护线程setDaemon(true)将t2线程标记为守护线程
package day30_多线程方法; //守护线程 public class Demo03 { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); MyThread2 t2 = new MyThread2(); t1.setName("女神线程"); t2.setName("(备胎)守护线程"); // 在调用start()方法前, // 调用setDaemon(true)将t2线程标记为守护线程 t2.setDaemon(true); t1.start(); t2.start(); } } class MyThread1 extends Thread { @Override public void run() { for (int i = 0; i <= 10; i++) { System.out.println(getName() + "@" + i); } } } class MyThread2 extends Thread { @Override public void run() { for (char c = 'A'; c <= 'Z'; c++) { System.out.println(getName() + "@" + c); } } }
Thread.yield();
package day30_多线程方法; public class Demo04 { public static void main(String[] args) { MyThread3 t1 = new MyThread3("线程1"); MyThread3 t2 = new MyThread3("线程2"); t1.start(); t2.start(); } } class MyThread3 extends Thread { public MyThread3(String string) { super(string); } @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println(getName() + "@" + i); // 让出当前的cpu的执行权,让结果更加均匀一点,也不是绝对均匀 // 当前的线程让出执行权后,再次进行抢夺资源也可能会抢到 Thread.yield(); } } }
内部类创建线程
package day30_多线程方法; public class Demo05 { public static void main(String[] args) { // 内部类创建线程 Thread t1 = new Thread() { public void run() { for (char i = 'a'; i < 'z'; i++) { System.out.println(getName() + "@" + i); Thread.yield(); } } }; Thread t2 = new Thread() { public void run() { for (char i = 65; i < 'z'; i++) { System.out.println(getName() + "@" + i); Thread.yield(); } } }; t1.start(); t2.start(); } }
t1.join()线程插队
package day30_多线程方法; //join方法 public class Demo06 { public static void main(String[] args) throws InterruptedException { // 如果想要先执行线程1,再执行main方法,使用join,插队 MyThread4 t1 = new MyThread4(); t1.setName("线程"); t1.start(); // 将线程t1添加到当前线程的前面执行 t1.join(); for (int i = 0; i < 100; i++) { System.out.println("main@" + i); } } } class MyThread4 extends Thread { @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println(getName() + "@" + i); } } }
线程安全
线程加锁是为了避免超卖问题,或者卖出同一张票的问题
加锁的方式有两种:
Synchronized锁
//线程安全 //多个线程实现先后依次访问共享的资源,解决线程安全 //加锁:每次只允许一个线程加锁,加锁后才可以访问资源,访问完毕后释放锁资源, //其他线程才可以抢占资源 //方案1:同步代码块,注意传入的锁的对象一定是唯一的 //synchronized(同步锁) { // //共享的资源 //} //方案2:同步方法,将访问的共享资源的核心方法上锁,以此来保证线程安全 //修饰符 synchronized 返回值类型 方法名(方法参数) { // //} //锁对象不能自己指定: // 普通方法锁对象:this // 静态方法锁对象:当前类的字节码文件对象
Lock锁
//方案3:为了更加清晰的表示如何加锁和解锁,JDK5以后提供了一个新的锁对象Lock //获取锁:void lock()获得锁,然后获得资源 //释放锁:void unlock()释放锁,然后释放资源
Demo01
package day31_线程安全; public class MyThread extends Thread { // 表示此成员变量为所有的对象共享 static int ticket = 0 ; // 锁对象唯一 static Object obj = new Object(); public MyThread(String string) { super(string); } // 线程在执行过程中,cpu的执行权可能随时会被抢走 @Override public void run() { // synchronized (obj) { while (true) { // 锁对象一定要唯一 synchronized (obj) { // synchronized (MyThread.class) { if (ticket >= 1000) { break; } ticket++; System.out.println(getName() + "正在卖票" + ticket + "张"); } } } }
package day31_线程安全; //线程加锁是为了防止超卖问题、或者卖出同一张票的问题 //加锁的方式有两种:关键字Synchronized,Lock对象 public class Demo01 { public static void main(String[] args) { MyThread t1 = new MyThread("窗口1"); MyThread t2 = new MyThread("窗口2"); MyThread t3 = new MyThread("窗口3"); t1.start(); t2.start(); t3.start(); } }
Demo02
package day31_线程安全; public class Demo02 { public static void main(String[] args) { MyThread2 t1 = new MyThread2("窗口1"); MyThread2 t2 = new MyThread2("窗口2"); MyThread2 t3 = new MyThread2("窗口3"); t1.start(); t2.start(); t3.start(); } }
package day31_线程安全; public class MyThread2 extends Thread { // 表示此成员变量为所有的对象共享 static int ticket; public MyThread2(String string) { super(string); } // 线程在执行过程中,cpu的执行权可能随时会被抢走 @Override public void run() { while (true) { if (method()) { break; } } } public static synchronized boolean method() { if (ticket >= 1000) { return true; } try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } ticket++; System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket + "张"); return false; } }
Demo03
package day31_线程安全; public class Demo03 { public static void main(String[] args) { MyRun m1 = new MyRun(); Thread t1 = new Thread(m1, "窗口1"); Thread t2 = new Thread(m1, "窗口2"); Thread t3 = new Thread(m1, "窗口3"); t1.start(); t2.start(); t3.start(); } }
package day31_线程安全; public class MyRun implements Runnable { // 表示此成员变量为所有的对象共享 static int ticket; // 线程在执行过程中,cpu的执行权可能随时会被抢走 @Override public void run() { while (true) { if (method()) { break; } } } public synchronized boolean method() { if (ticket >= 2000) { return true; } try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } ticket++; System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket + "张"); return false; } }
Demo04
package day31_线程安全; public class Demo04 { public static void main(String[] args) { MyThread3 t1 = new MyThread3("窗口1"); MyThread3 t2 = new MyThread3("窗口2"); MyThread3 t3 = new MyThread3("窗口3"); t1.start(); t2.start(); t3.start(); } }
package day31_线程安全; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyThread3 extends Thread { // 表示此成员变量为所有的对象共享 static int ticket; static Lock lock = new ReentrantLock(); public MyThread3(String string) { super(string); } // 线程在执行过程中,cpu的执行权可能随时会被抢走 @Override public void run() { while (true) { try { lock.lock(); if (ticket >= 2000) { break; } try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } ticket++; System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket + "张"); } finally { lock.unlock(); } } } }
线程状态及常用方法
在Java程序中,一个线程对象通过调用start()方法启动线程,并且在线程获取CPU时,自动执行run()方法。run()方法执行完毕,代表线程的生命周期结束。
●在整个线程的生命周期中,线程的状态有以下6种:
○New:新建状态,新创建的线程,此时尚未调用start()方法;
○Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法;
○Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
○Waiting:等待状态,运行中的线程,因为join()等方法调用,进入等待;
○Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;
○Terminated:终止状态,线程已终止,因为run()方法执行完毕。
●当线程启动后,它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换,直到最后变成Terminated状态,线程终止
线程终止的原因有:
○线程正常终止:run()方法执行到return语句返回;
○线程意外终止:run()方法因为未捕获的异常导致线程终止;
○对某个线程的Thread实例调用stop()方法强制终止(宇宙超级无敌强烈不推荐);
NEW新建状态
package day31_线程状态; //NEW //新线程创建出来,还没有调用start方法,此时状态为新建状态 public class New { public static void main(String[] args) { Thread t1 = new Thread() { @Override public void run() { System.out.println("子线程正在执行任务"); } }; System.out.println(t1.getState()); } }
RUNNABLE可运行状态
package day31_线程状态; //RUNNABLE //线程对象调用start方法,等待Cpu分配资源RUNNABLE状态 public class Runable { public static void main(String[] args) { Thread t1 = new Thread() { @Override public void run() { System.out.println("子线程正在执行任务"); } }; t1.start(); System.out.println(t1.getState()); } }
TERMINATED终止状态
package day31_线程状态; //TERMINATED终止状态,run()方法执行完毕,操作系统可能已经注销了此线程 public class Terminated { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread() { @Override public void run() { System.out.println("子线程正在执行任务"); } }; t1.start(); Thread.sleep(1000); System.out.println(t1.getState()); } }
WAITING等待状态
package day31_线程状态; //WAITING等待状态 wait() join() public class WaitingState01 { public static void main(String[] args) throws InterruptedException { Runnable run = new Runnable() { Object obj = new Object(); @Override public void run() { synchronized (obj) { try { obj.wait();// 等待 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; Thread t1 = new Thread(run); t1.start(); Thread.sleep(1000); System.out.println(t1.getState()); } }
package day31_线程状态; //WAITING等待状态 public class WaitingState02 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread() { @Override public void run() { while (true) { } } }; t1.start(); // t1线程插队到当前的主线程的前面,主线程处于等待状态 System.out.println("111"); t1.join(); System.out.println("222"); // 获取不到,因为当前的主线程被t1线程给插队,t1为死循环,无法结束任务,主线程处于WATTING状态 System.out.println(Thread.currentThread().getState()); } }
package day31_线程状态; //WAITING状态 等待状态 wait() join() //wait() //1.和synchronized同时存在 //2.锁对象和调用wait方法的需要是同一个 //notify() 唤醒--锁对象的让等待的对象 public class WaitingState03 { public static void main(String[] args) throws InterruptedException { Object object = new Object(); Runnable run = new Runnable() { @Override public void run() { synchronized (this) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("正在执行"); } } }; Thread t1 = new Thread(run); t1.start(); Thread.sleep(1000); System.out.println(t1.getState()); } }
BLOCKED阻塞状态
package day31_线程状态; //BLOCKED public class Blocked { public static void main(String[] args) throws InterruptedException { Runnable run = new Runnable() { @Override public void run() { synchronized (this) { // 死循环 while(true){ } } } }; // 创建线程t1和t2并启动子线程 Thread t1 = new Thread(run, "子线程1"); Thread t2 = new Thread(run, "子线程2"); t1.start(); t2.start(); // 主线程休眠,让t1和t2同时竞争一个锁资源 Thread.sleep(1000); System.out.println(t1.getName() + t1.getState()); System.out.println(t2.getName() + t2.getState()); } }
TIMED_WAITING计时等待状态
package day31_线程状态; //sleep() 不释放锁资源 //wait() 释放锁资源 public class Timed_Waiting01 { public static void main(String[] args) throws InterruptedException { Runnable run = new Runnable() { @Override public void run() { synchronized (this) { try { Thread.sleep(5000); // this.wait(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; Thread t1 = new Thread(run, "线程1"); Thread t2 = new Thread(run, "线程2"); t1.start(); t2.start(); // 让主线程等待 Thread.sleep(1000); System.out.println(t1.getName() + t1.getState()); System.out.println(t2.getName() + t2.getState()); } }
package day31_线程状态; //TIMED_WAITING 线程计时等待状态 //join(参数),可以将线程切换为计时等待状态 //sleep() 不释放锁资源 //wait() 释放锁资源 public class Timed_Waiting02 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread() { @Override public void run() { Thread t2 = new Thread() { @Override public void run() { while (true) { } } }; t2.start(); System.out.println("t2的状态计时等待前" + t2.getState()); // join方法让当前线程处于计时等待 try { t2.join(5000 * 1000); System.out.println("t2的状态计时等待后" + t2.getState()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t1.start(); Thread.sleep(1000); System.out.println("t1的状态" + t1.getState()); } }
线程池
线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。
┌─────┐ execute ┌──────────────────┐ │Task1│─────────>│ThreadPool │ ├─────┤ │┌───────┐┌───────┐│ │Task2│ ││Thread1││Thread2││ ├─────┤ │└───────┘└───────┘│ │Task3│ │┌───────┐┌───────┐│ ├─────┤ ││Thread3││Thread4││ │Task4│ │└───────┘└───────┘│ ├─────┤ └──────────────────┘ │Task5│ ├─────┤ │Task6│ └─────┘ ...
线程池常用类和接口
在Java标准库提供了如下几个类或接口,来操作并使用线程池:
1.ExecutorService接口:进行线程池的操作访问;
2.Executors类:创建线程池的工具类;
3.ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;
线程池的基本使用方式
// 线程池基本使用方式 // 创建一个ThreadPoolExecutor类型的对象,代表固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(3); // 该线程池拥有3个线程 // 执行线程任务 executor.execute(task1); executor.execute(task2); executor.execute(task3); executor.execute(task4); executor.execute(task5); // 使用结束后,使用shutdown关闭线程池 executor.shutdown();
线程池的常见方法
● 执行无返回值的线程任务:void execute(Runnable command);
● 提交有返回值的线程任务:Future submit(Callable task);
● 关闭线程池:void shutdown(); 或 shutdownNow();
● 等待线程池关闭:boolean awaitTermination(long timeout, TimeUnit unit);
执行线程任务
execute()和submit()提交任务时的区别?
execute()只能提交Runnable类型的任务,没有返回值,而submit()既能提交Runnable类型任务也能提交Callable类型任务,可以返回Future类型结果,用于获取线程任务执行结果。
execute()方法提交的任务异常是直接抛出的,而submit()方法是捕获异常,当调用Future的get()方法获取返回值时,才会抛出异常。
// 计算1-100w的之间所有数字的累加和,每10w个数字交给1个线程处理 // 创建一个固定大小的线程池: ExecutorService executorService = Executors.newFixedThreadPool(4); // 创建集合,用于保存Future执行结果 List<Future<Integer>> futureList = new ArrayList<Future<Integer>>(); // 每10w个数字,封装成一个Callable线程任务,并提交给线程池 for (int i = 0; i <= 900000; i += 100000) { Future<Integer> result = executorService.submit(new CalcTask(i+1, i + 100000)); futureList.add(result); } // 处理线程任务执行结果 try { int result = 0; for (Future<Integer> f : futureList) { result += f.get(); } System.out.println("最终计算结果" + result); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } // 关闭线程池 pool.shutdown();
关闭线程池
线程池在程序结束的时候要关闭。
①使用shutdown()方法关闭线程池的时候,它会等待正在执行的任务先完成,然后再关闭。 ②shutdownNow()会立刻停止正在执行的任务; ③使用awaitTermination()方法时,主线程会处于一种等待的状态,按照指定的timeout检查线程池。 第一个参数指定的是时间, 第二个参数指定的是时间单位(当前是秒)。返回值类型为boolean型。 如果等待的时间超过指定的时间,但是线程池中的线程运行完毕,awaitTermination()返回true。 如果等待的时间超过指定的时间,但是线程池中的线程未运行完毕,awaitTermination()返回false。 如果等待时间没有超过指定时间,则继续等待。 该方法经常与shutdown()方法配合使用,用于检测线程池中的任务是否已经执行完毕:
线程池的执行流程
1.提交一个新线程任务,线程池会在线程池中分配一个空闲线程,用于执行线程任务;
2.如果线程池中不存在空闲线程,则线程池会判断当前“存活的线程数”是否小于核心线程数corePoolSize。
○如果小于核心线程数corePoolSize,线程池会创建一个新线程(核心线程)去处理新线程任务;
○如果大于核心线程数corePoolSize,线程池会检查工作队列;
■如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,将从工作队列中按照FIFO的规则取出1个线程任务并分配执行;
■如果工作队列已满,则判断线程数是否达到最大线程数maximumPoolSize;
●如果当前“存活线程数”没有达到最大线程数maximumPoolSize,则创建一个新线程(非核心线程)执行新线程任务;
●如果当前“存活线程数”已经达到最大线程数maximumPoolSize,直接采用拒绝策略处理新线程任务;
综上所述,执行顺序为:核心线程、工作队列、非核心线程、拒绝策略。
线程池的配置参数
参数1:int corePoolSize, 指定线程池的核心线程数量 正式员工 参数2:int maximumPoolSize, 指定线程池的最大线程数量 正式员工 临时工 参数3:long keepAliveTime, 指定临时线程的最大存活时间 临时工空闲多久被开除 参数4:TimeUnit unit, 临时线程最大的存活时间单位 参数5:BlockingQurue workQueue, 指定线程池的任务队列(客户排队的地方) 参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr) 参数7:RejectedExecutionHandler handler 线程池的拒绝策略
参数1:corePoolSize线程池核心线程数:也可以理解为线程池维护的最小线程数量,核心线程创建后不会被回收。大于核心线程数的线程,在空闲时间超过keepAliveTime后会被回收;
在创建了线程池后,默认情况下,线程池中并没有任何线程,当调用 execute() 方法添加一个任务时,如果正在运行的线程数量小于corePoolSize,则马上创建新线程并运行这个任务。
IO密集计算:由于 I/O 设备的速度相对于 CPU来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算。最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]。
CPU密集型:CPU 密集型计算大部分场景下都是纯 CPU 计算,多线程主要目的是提升CPU利用率,最佳线程数 =“CPU 核心数 +1”。这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以临时替补,从而保证 CPU 的利用率。
参数2:maximumPoolSize线程池最大线程数:线程池允许创建的最大线程数量;(包含核心线程池数量)
参数3:keepAliveTime非核心线程线程存活时间:当一个可被回收的线程的空闲时间大于keepAliveTime,就会被回收。
当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会被回收,直到线程池中的线程数不超过corePoolSize。
如果设置allowCoreThreadTimeOut = true,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
参数4:TimeUnit时间单位:参数keepAliveTime的时间单位;
参数5:BlockingQueue阻塞工作队列:用来存储等待执行的任务;
参数6:ThreadFactory线程工厂 : 用于创建线程,以及自定义线程名称,需要实现ThreadFactory接口;
参数7:RejectedExecutionHandler拒绝策略:当线程池线程内的线程耗尽,并且工作队列达到已满时,新提交的任务,将使用拒绝策略进行处理;
四种拒绝策略
ThreadPoolExecutor.AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常;
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常;
ThreadPoolExecutor.DiscardOldestPolicy:丢弃工作队列中的队头任务(即最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
ThreadPoolExecutor.CallerRunsPolicy:由原调用线程处理该任务 (谁调用,谁处理);
线程池的优点
1.提高响应速度
2.降低线程的可管理行
3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务)
创建线程池的方式
创建无上限的线程池
线程池是一个可以复用线程的技术
ExecutorService
方式1:Executors工具类创建线程对象
FixedThreadPool
package day31_线程池; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo02 { public static void main(String[] args) throws InterruptedException { // 获取有上限的线程池对象 // 计算密集型任务:核心线程数量=CPU核数+1 // IO密集型任务:CPU*2 ExecutorService pool1 = Executors.newFixedThreadPool(3); // 提交任务 pool1.submit(new MyRunnable()); Thread.sleep(1000); System.out.println("=========1========="); pool1.submit(new MyRunnable()); Thread.sleep(1000); System.out.println("=========2========="); pool1.submit(new MyRunnable()); Thread.sleep(1000); System.out.println("=========3========="); pool1.submit(new MyRunnable()); Thread.sleep(1000); System.out.println("=========4========="); pool1.submit(new MyRunnable()); pool1.shutdown(); } }
CachedThreadPool
package day31_线程池; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //创建无上限的线程池 //线程池是一个可以复用线程的技术 //ExecutorService //方式1:Executors工具类创建线程对象 public class Demo01 { public static void main(String[] args) throws InterruptedException { // 获取线程池 ExecutorService pool = Executors.newCachedThreadPool(); // 提交线程任务 pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); pool.submit(new MyRunnable()); Thread.sleep(1000);// 线程休眠1秒,体现复用 System.out.println("============"); pool.submit(new MyRunnable()); // 关闭线程池 pool.shutdown(); } }
SingleThreadExecutor
package day32_线程池; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Demo04 { public static void main(String[] args) { //单例线程池 单独线程的ThreadPoolExecutor对象 ExecutorService pool = Executors.newSingleThreadExecutor(); pool.execute(new MyRun()); pool.execute(new MyRun()); pool.execute(new MyRun()); pool.execute(new MyRun()); pool.execute(new MyRun()); } }
ScheduledThreadPool
package day32_线程池; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class Demo05 { public static void main(String[] args) { // 能实现定时、周期性任务的线程池 Runnable run = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); // 延时5秒执行任务 // 参数1:任务 2:延时多久 3:时间单位 // pool.schedule(run, 5, TimeUnit.SECONDS); // 延时5秒执行此任务,后每过3秒重复执行此任务 pool.scheduleAtFixedRate(run, 5, 3, TimeUnit.SECONDS); pool.shutdown(); } }
方式2:ThreadPoolExecutor
package day32_线程池; public class MyRun implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "正在执行"); // try { // //线程执行的时间长一点,便于结果的观察 // Thread.sleep(Integer.MAX_VALUE); // } catch (InterruptedException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } }
package day32_线程池; import java.util.concurrent.Callable; public class MyCall implements Callable<Integer> { private int n; public MyCall(int n) { this.n = n; } @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i <= n; i++) { sum += i; } return sum; } }
01
package day32_线程池; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; //创建无上限的线程池 //线程池是一个可以复用线程的技术 //ExecutorService //方式1:Executors工具类创建线程对象 // FixedThreadPool // CachedThreadPool // SingleThreadExecutor // ScheduledThreadPool //方式2:ThreadPoolExecutor //参数1:int corePoolSize, 指定线程池的核心线程数量 正式员工 //参数2:int maximumPoolSize, 指定线程池的最大线程数量 正式员工 临时工 //参数3:long keepAliveTime, 指定临时线程的最大存活时间 临时工空闲多久被开除 //参数4:TimeUnit unit, 临时线程最大的存活时间单位 //参数5:BlockingQurue workQueue, 指定线程池的任务队列(客户排队的地方) //参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr) //参数7:RejectedExecutionHandler handler 线程池的拒绝策略 //四种拒绝策略: //AbortPolicy() 丢弃当前线程任务并抛出异常,是默认的策略 //DiscardPolicy() 丢弃当前线程任务但不抛出异常[不推荐的做法] //DiscardOldestPolicy() 丢弃等待队列中的最老的线程任务,然后将当前任务添加到队列中 //CallerRunsPolicy() 直接由"当前调用线程"直接执行该线程任务,绕过线程池直接执行 //线程池优点: //1.提高响应速度 //2.降低线程的可管理行 //3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务) public class Demo01 { public static void main(String[] args) throws InterruptedException { // 参数1:int corePoolSize, 指定线程池的核心线程数量 正式员工 // 参数2:int maximumPoolSize, 指定线程池的最大线程数量 正式员工 临时工 // 参数3:long keepAliveTime, 指定临时线程的最大存活时间 临时工空闲多久被开除 // 参数4:TimeUnit unit, 临时线程最大的存活时间单位 // 参数5:BlockingQurue workQueue, 指定线程池的任务队列(客户排队的地方) // 参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr) // 参数7:RejectedExecutionHandler handler 线程池的拒绝策略 ExecutorService pool1 = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); // 2.提交任务 MyRun run = new MyRun(); pool1.submit(run);// 提交任务给线程池对象,线程池会自动的创建一个新的线程,自动处理任务 pool1.submit(run); pool1.submit(run); pool1.submit(run);// 提交第4个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务 pool1.submit(run);// 提交第5个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务 pool1.submit(run);// 提交第6个任务时,核心线程满,队列也满 // Thread.sleep(1000);//任务短 pool1.submit(run);// 提交第7个任务时,创建临时线程1 pool1.submit(run);// 提交第8个任务时,创建临时线程2 pool1.submit(run);// 提交第9个任务时,创建临时线程3 此时核心线程满,队列也满,临时线程满 pool1.submit(run);// 再来新的任务会进行拒绝策略 pool1.submit(run);// 再来新的任务会进行拒绝策略 pool1.submit(run);// 再来新的任务会进行拒绝策略 // 等线程池中的内容执行完毕关闭线程池,不接收新任务,但仍处理工作队列中的任务 pool1.shutdown(); // 立即关闭线程池,不接收新任务,同时中断正在执行的任务 // pool1.shutdownNow(); } }
02
package day32_线程池; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; //创建无上限的线程池 //线程池是一个可以复用线程的技术 //ExecutorService //方式1:Executors工具类创建线程对象 //方式2:ThreadPoolExecutor //四种拒绝策略: //AbortPolicy() 丢弃当前线程任务并抛出异常,是默认的策略 //DiscardPolicy() 丢弃当前线程任务但不抛出异常[不推荐的做法] //DiscardOldestPolicy() 丢弃等待队列中的最老的线程任务,然后将当前任务添加到队列中 //CallerRunsPolicy() 直接由"当前调用线程"直接执行该线程任务,绕过线程池直接执行 //线程池优点: //1.提高响应速度 //2.降低线程的可管理行 //3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务) public class Demo02 { public static void main(String[] args) throws InterruptedException, ExecutionException { // 参数1:int corePoolSize, 指定线程池的核心线程数量 正式员工 // 参数2:int maximumPoolSize, 指定线程池的最大线程数量 正式员工 临时工 // 参数3:long keepAliveTime, 指定临时线程的最大存活时间 临时工空闲多久被开除 // 参数4:TimeUnit unit, 临时线程最大的存活时间单位 // 参数5:BlockingQurue workQueue, 指定线程池的任务队列(客户排队的地方) // 参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr) // 参数7:RejectedExecutionHandler handler 线程池的拒绝策略 ExecutorService pool1 = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); // 2.提交任务 Future f1 = pool1.submit(new MyCall(5)); Future f2 = pool1.submit(new MyCall(100)); Future f3 = pool1.submit(new MyCall(200)); Future f4 = pool1.submit(new MyCall(300)); Future f5 = pool1.submit(new MyCall(400)); Future f6 = pool1.submit(new MyCall(500)); System.out.println(f1.get()); System.out.println(f2.get()); System.out.println(f3.get()); System.out.println(f4.get()); pool1.execute(new MyRun()); pool1.execute(new MyRun()); pool1.execute(new MyRun()); // 等线程池中的内容执行完毕关闭线程池,不接收新任务,但仍处理工作队列中的任务 pool1.shutdown(); } }
03
package day32_线程池; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; //创建无上限的线程池 //线程池是一个可以复用线程的技术 //ExecutorService //方式1:Executors工具类创建线程对象 //方式2:ThreadPoolExecutor //四种拒绝策略: //AbortPolicy() 丢弃当前线程任务并抛出异常,是默认的策略 //DiscardPolicy() 丢弃当前线程任务但不抛出异常[不推荐的做法] //DiscardOldestPolicy() 丢弃等待队列中的最老的线程任务,然后将当前任务添加到队列中 //CallerRunsPolicy() 直接由"当前调用线程"直接执行该线程任务,绕过线程池直接执行 //线程池优点: //1.提高响应速度 //2.降低线程的可管理行 //3.降低资源消耗(线程执行完后,不销毁,可以执行其他的任务) public class Demo03 { public static void main(String[] args) throws InterruptedException, ExecutionException { // 参数1:int corePoolSize, 指定线程池的核心线程数量 正式员工 // 参数2:int maximumPoolSize, 指定线程池的最大线程数量 正式员工 临时工 // 参数3:long keepAliveTime, 指定临时线程的最大存活时间 临时工空闲多久被开除 // 参数4:TimeUnit unit, 临时线程最大的存活时间单位 // 参数5:BlockingQurue workQueue, 指定线程池的任务队列(客户排队的地方) // 参数6:ThreadFactory threadFactory, 指定线程工厂(负责招聘员工的hr) // 参数7:RejectedExecutionHandler handler 线程池的拒绝策略 // 参数6:线程工厂自定义线程名称 ThreadFactory factory = new ThreadFactory() { private int count = 1; @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "线程名-" + count++); return thread; } }; // 参数7:线程池的拒绝策略定义 RejectedExecutionHandler policy = new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println("任务" + r + "线程满队列满不能执行了"); } }; ExecutorService pool1 = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3), factory, policy); // 2.提交任务 MyRun run = new MyRun(); pool1.submit(run);// 提交任务给线程池对象,线程池会自动的创建一个新的线程,自动处理任务 pool1.submit(run); pool1.submit(run); pool1.submit(run);// 提交第4个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务 pool1.submit(run);// 提交第5个任务时,没有达到临时线程的创建条件,复用核心线程,执行此任务 pool1.submit(run);// 提交第6个任务时,核心线程满,队列也满 // Thread.sleep(1000);//任务短 pool1.submit(run);// 提交第7个任务时,创建临时线程1 pool1.submit(run);// 提交第8个任务时,创建临时线程2 pool1.submit(run);// 提交第9个任务时,创建临时线程3 此时核心线程满,队列也满,临时线程满 pool1.submit(run);// 再来新的任务会进行拒绝策略 // 等线程池中的内容执行完毕关闭线程池,不接收新任务,但仍处理工作队列中的任务 pool1.shutdown(); } }