多线程基础
一、创建多线程的方法
1.通过继承Thread类实现多线程
class myThread extends Thread{ public myThread(String name){ super(name); } @Override public void run() { int i=0; while(i++ < 5){ System.out.println(Thread.currentThread()+" is running"); } } } public static void main(String[] args) { //继承Thread实现多线程 myThread t1 = new myThread("线程1"); t1.start(); myThread t2 = new myThread("线程2"); t2.start(); }
2.Runnable接口实现多线程
Runnable接口只有一个抽象的run方法
//使用Runnable接口实现多线程 Thread t3 = new Thread(new Runnable() { @Override public void run() { int i=0; while(i++ < 5){ System.out.println(Thread.currentThread()+" is running"); } } },"线程3");t3.start(); //lambda表达式 Thread t4 = new Thread(()-> { int i=0; while(i++ < 5){ System.out.println(Thread.currentThread()+" is running"); } },"线程4");t4.start();
3.Callable接口实现多线程
//callable接口实现多线程,该方法的好处是有返回值 FutureTask<Object> ft =new FutureTask<Object>(()->{ int i=0; while(i++ < 5){ System.out.println(Thread.currentThread()+" is running"); } return 1; }); Thread t5 = new Thread(ft,"进程5"); t5.start(); try { //得到返回值 Object result = ft.get(); System.out.println(result); } catch (Exception e) { e.printStackTrace(); }
Callable相比于前面两种方法的好处是有返回值
方法声明 | 功能描述 |
boolean cancel(boolean mayInterruptIfRunning) | 用于取消任务,参数表示是否允许取消正在执行却没有执行完毕的任务,true表示取消可以正在执行的任务 |
boolean isCancelled() | 判断任务是否被成功取消,如果正常完成前被取消,则返回true |
boolean isDone() | 判断任务是否已经完成,若任务完成返回true |
T get | 用于获取执行结果,这个任务会发生阻塞,需等到任务执行完毕才返回执行结果 |
T get(long timeout,TimeUnit unit) |
指定时间获取结果,在指定时间没有获取结果,则返回null |
二、线程的生命周期及状态转换
当线程任务代码正常执行完毕或者线程抛出一个未捕获的异常(Exception)或者错误(Error)时,线程生命周期便会结束
线程的生命周期分为6个状态,`NEW`(新建状态)、`RUNNABLE`(可运行状态)、`BLOCKED`(阻塞状态)、`WAITING`(等待状态)、`TIMED_WAITING`(定时等待状态)和`TERMINATED`(终止状态)。
1.`NEW`(新建状态)
创建一个线程对象后,该线程对象就处于新建状态,此时不能允许,仅仅由jvm分配内存,没有表现出线程的动态特征
2.`RUNNABLE`(可运行状态)
当线程调用了start方法,此时就会从新建状态转换为可运行状态,`RUNNABLE`(可运行状态)可细分为两个状态:READY(就绪状态)、RUNNABLE(允许状态)
,并且线程可在这两个状态间相互转换
READY(就绪状态):线程对象调用start方法,等待JVM调度,此时线程并没有执行
RUNNABLE(允许状态):线程对象获得JVM调度,如果存在多个CPU,那么就允许多个线程并行运行
3.`BLOCKED`(阻塞状态)
处于运行状态的线程可能会因为某些原因失去CPU的执行权,暂时停止运行进入阻塞状态,此时JVM不会给线程分配CPU,直到线程从新进入就绪状态,才有机会转换到运行状态。阻塞状态只能先进入就绪状态,才能再进入运行状态
线程一般会再两个情况下进入阻塞状态:
1. 当线程A运行过程种,试图获取同步锁,却被B线程获取,此时JVM把当前线程A存放到对象的锁池中,线程A就进入阻塞状态
2. 当线程运行过程中,发出I/O请求时,此时该线程也会进入阻塞状态
4.`WAITING`(等待状态)
当运行状态的线程调用了无时间参数限制后,如wait()、join()等方法就会使处于运行状态的线程转换成等待状态
处于等待状态的线程不能立即抢夺CPU的使用权,必须等待其他线程执行特定的操作后,才有机会再次争夺CPU的使用权,将等待状态的线程转换成运行状态
5、`TIMED_WAITING`(定时等待状态)
将运行状态中的线程转换为定时等待状态中的线程,方法与转换为等待状态类似,只是增加了时间参数
6、`TERMINATED`(终止状态)
线程的run方法、call方法正常执行完毕或者线程抛出一个未捕获的异常或者错误,线程就会进入终止状态,一旦进入终止状态,线程不在拥有运行的资格,也不再转换到其他状态,生命周期结束
三、线程的调度
1.线程的优先级
Thread类的静态窗帘 | 功能描述 |
static int MAX_PRIORITY | 表示线程的最高优先级,相当于10 |
static int MIN_PRIORITY | 表示线程的最低优先级,相当于1 |
static int NORM_PRIORITY | 表示线程的普通优先级,相当于5 |
Thread t1 = new Thread(new Runnable() { @Override public void run() { int i=0; while(i++ < 5){ System.out.println(Thread.currentThread()+" "+i); } } },"t1"); Thread t2 = new Thread(()->{ int i=0; while(i++ < 5){ System.out.println(Thread.currentThread()+" "+i); } },"t2"); //设置优先级 t1.setPriority(Thread.MAX_PRIORITY); t2.setPriority(Thread.MIN_PRIORITY); t1.start(); t2.start();
虽然java提供了10个线程优先级,但这些优先级需要操作系统的支持,不同操作系统的支持是不一样的,不能很好的和java中线程优先级一一对应,因此,在设计多线程应用程序时,其功能的实现一点不能依赖于线程的优先级,而是把线程优先级作为一中提高程序效率的手段。
2.线程休眠
@Override public void run() { int i=0; while(i++ < 5){ //线程t1等于2时休眠0.2秒 if(i == 2){ try{ Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread()+" "+i); } } },"t1");
线程Thread提供了两种线程休眠的方法,sleep(long millis)和sleep(long millis ,int nanos)这两种方法都带有休眠时间参数,必须等待休眠时间结束,线程才会转换到就绪状态
四、线程让步
Thread t2 = new Thread(()->{ int i=0; while(i++ < 5){ System.out.println(Thread.currentThread()+" "+i); } },"t2"); Thread t3 = new Thread(()->{ int i=0; while(i++ < 5){ if(i == 2){ System.out.println("线程让步"); Thread.yield();//线程做出让步 } System.out.println(Thread.currentThread()+" "+i); } },"t3"); t2.start(); t3.start();
注意sleep集合yield方法有点类似,都可以让当前线程暂停,但是yield不会阻塞线程,他只是将线程转换成就绪状态,让调度器重新调度一次,由于JVM默认采用抢占式资源调度模型,所有线程都会再次抢占CPU资源使用权,所以在执行线程让步后并不能保证立即执行其他线程
五、线程插队
Thread t4 = new Thread(new Runnable() { @Override public void run() { int i=0; while(i++ < 5){ System.out.println(Thread.currentThread().getName()+" "+i); } } },"t4"); t4.start(); for(int i = 1; i < 6; i++){ System.out.println(Thread.currentThread().getName()+" "+i); if(i==2){ t4.join(); } }
当线程插队优先执行时,整个程序执行完毕后才会执行其他线程。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构