概述
- 线程:进程可进一步细化为线程,是一个进程内部的一条执行路径。
- 若一个进程同一时间并行执行多个线程,就是支持多线程的
- 线程作为最小的调度和执行单位,每个线程拥有独立的运行栈和程序计数器(PC),线程的切换开销小
- 多个线程操作共享的资源有安全隐患
线程的创建
-
方式一:继承于Thread类
- 创建一个继承于Thread类的子类
- 重写Thread类的run()
- 创建Thread类的子类的对象
- 通过此对象调用start()
- 启动当前线程
- 调用当前线程的run()
- 问题:
- 不能调用run();方法;
- 一个Thread对象只能调用一次start();方法
-
创建多线程方法二:实现Runnable接口
- 创建一个实现了Runnable接口的类
- 实现类去实现Runable中的抽象方法:run();
- 创建实现类的对象
- 讲创建对象作为参数传递到Thread类的构造器中,创建Thread类的对象
- 通过Thread类的对象调用start();
-
创建线程方法三:实现Callable接口 (JDK5.0 新增)
package com.zhh.java2; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程 * 方式强大 * 1. call()可以有返回值 * 2. call()可以抛出异常,获取异常信息 * 3. Callable是支持泛型的· */ /** * * @description 实现callable --->JDK5.0 * @author 阳光不燥,微风正好 Email:20901674@qq.com * @version IDEA 2021.3 JDK8 * @date 2022/1/11 19:03 * */ //1. 创建一个实现Callable的实现类 class NumThread implements Callable{ //2.实现call方法,将此线程需要执行的操作定义在call方法中 @Override public Object call() throws Exception { int sum=0; for (int i = 0; i < 100; i++) { if(i%2==0){ System.out.println(i); sum+=i; } } return sum; } } public class thread2 { public static void main(String[] args) { //3.创建Callable接口实现类的对象 NumThread numThread=new NumThread(); //4.将此Callable接口实现类的对象作为传递到FutureTask //构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); //5. 将FutureTask的对象作为参数传递到Thread类的构造其中,创建 //Thread线程并启动 new Thread(futureTask).start(); try { //6.获取Callable中call方法的返回值 //get()返回值即为Futuresk构造器参数Collable实现类重写的call()的返回值 Object sum = futureTask.get(); System.out.println(sum); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }
-
创建线程方法四:线程池(JDK5.0 新增)
-
思路:体现创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的交通工具
-
好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建
- 便于线程管理
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
-
线程池相关API
- EcecutorServise:真正的线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task); 执行任务,没有返回值,一般又来执行Callable
- void shutdown(); 关闭连接池
- Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
- Executors.newCachedThreadPool();创建一个可根据需要创建新线程的线程池
- Executors.newFixedThreadPool(n);创建一个可重用固定线程数的线程池
- Executors.newSingleThreadPool();创建一个只有一个线程的线程池
- Executors.newScheuledThreadPool(n); 创建一个线程池,他可安排在给定延迟后运行命令或者定期地执行
- EcecutorServise:真正的线程池接口。常见子类ThreadPoolExecutor
-
package com.zhh.java2; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * * @description 线程创建方法四:线程池创建 * @author 阳光不燥,微风正好 Email:20901674@qq.com * @version IDEA 2021.3 JDK8 * @date 2022/1/13 11:22 * */ class myThread2 implements Runnable{ @Override public void run() { for (int i=0;i<100;i++){ if(i%2==0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } } public class threadPoll { public static void main(String[] args) { //提供指定数量的线程数的线程池 ExecutorService service = Executors.newFixedThreadPool(10); service.execute(new myThread2()); //适合用于Runnable // service.submit();//适用于Callable service.shutdown();//关闭线程池 } }
-
-
比较继承Thread类和实现Runnable接口的两种方式
- 开发中:优先选择:实现Runnable接口的方式
- 原因:实现的方式更适合来处理多个线程有共享数据的情况
- 联系:public class Thread implements Runabnle
- 相同点:两种方式都需要重写run()方法
- 开发中:优先选择:实现Runnable接口的方式
-
Thread类的方法
- start(); 启动当前线程;调用当前线程的run();
- run(); 通常需要重写Thread类中的此方法·,将创建的线程要执行的操作声明在此方法中
- currentThread();静态方法,返回当前代码执行的线程
- getName();获取当前线程的名字
- setName();设置当前线程的名字
- yield();释放当前cpu的执行权
- join();在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行以后,线程a才结束阻塞状态。
- stop();已过时,当执行此方法时,强制结束当前线程
- sleep(long millitime); 让当前线程“睡眠”指定的millitime毫秒,当前线程是阻塞状态
- isAlive();判断当前线程是否存活
线程的调度
- 调度策略
- 时间片
- 抢占式:高优先级的线程抢占CPU
- Java调度方法
- 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
- 对高优先级,使用优先调度的抢占式策略
- 线程的优先级
- MAX_PRIORITY:10
- MIN_PRIORITY:1
- NORM_PRIORITY:5
- 如何获取和设置当前线程的优先级
- getPriority(); //获取优先级
- setPriority(int p); //获取优先级
- 说明:高优先级的线程要抢占低优先级线程cpu的执行权,但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味这只有当高优先级的线程执行完以后,低优先级的线程才执行