💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] # 线程设置名字 建议对每个线程启动之前进行定义,不建议启动后和为不同线程设置重名 可以使用Thread类的下面的方法 * 构造方法: `public Thread(Runnable target, String name);` * 设置名字: `public final void setName(String name);` * 取得名字: `public final String getName();` * 取得当前线程对象: `public static Thread currentThread();` 如果在实例化Thread类对象时候没有设置名字,那么会自动进行编号命名,保证不重复 # 线程的休眠 休眠方法 ~~~ public static void sleep(long millis) throws InterruptedException ~~~ 会抛出一个中断异常 ~~~ Thread.sleep(2000); ~~~ # 线程的优先级 优先级越高,越有可能先执行,只是大概率先执行,并不是一定 在Thread类提供 ~~~ * 设置优先级: public final void setPriority(int newPriority); * 取得优先级: public final int getPriority(); ~~~ 发现设置和取得优先级都是使用了int数据类型,对于此内容有三种取值: ~~~ * 最高优先级: public static final int MAX_PRIORITY; //10 * 中等优先级: public static final int NORM_PRIORITY; //5 * 最低优先级: public static final int MIN_PRIORITY; //1 ~~~ 例子 ~~~ Thread thread1 = new Thread(task1, "task1线程"); Thread thread2 = new Thread(task2, "task2线程"); thread1.setPriority(Thread.MAX_PRIORITY); thread1.setPriority(Thread.MIN_PRIORITY); thread1.start(); thread2.start(); ~~~ 主线程的优先级是多少? ~~~ public static void main(String[] args) { System.out.println(Thread.currentThread().getPriority()); //5,中等 } ~~~ # yield休眠当前线程 休眠当前线程, 是线程编程可执行状态,以便其他相同优先级的线程有机会执行, 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。 注意: 再次执行调度程序决定的, 可以立刻会再次执行。。。 yield会释放资源锁吗? yield不会释放资源所,当暂停后,等待调度程序再次调用,走完同步区域代码后才释放锁 ~~~ package com.study; public class Test01 implements Runnable { private String name; public Test01(String name) { this.name = name; } @Override public synchronized void run() { System.out.println(name + "-> start"); for (int i = 0; i < 1000; i++) { if (i % 2 == 0) { Thread.yield(); } System.out.println(Thread.currentThread().getName() + " --- i = " + (i)); } System.out.println(name + "-> stop"); } public static void main(String[] args) { Test01 a = new Test01("A"); Thread thread1 = new Thread(a); Thread thread2 = new Thread(a); Thread thread3 = new Thread(a); thread1.start(); thread2.start(); thread3.start(); } } ~~~ # join线程之间的并行执行变为串行执行 ~~~ public final void join(long millis) throws InterruptedException public final void join(long millis, int nanos) throws InterruptedException public final void join() throws InterruptedException ~~~ Thread类中的join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行。 join的意思是使得放弃当前线程的执行,并返回对应的线程 ~~~ package com.study; public class Test01 { public static void main(String[] args) throws InterruptedException { ThreadJoinTest t1 = new ThreadJoinTest("小明"); ThreadJoinTest t2 = new ThreadJoinTest("小冬"); t1.start(); /** * 程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,并返回t1线程继续执行直到线程t1执行完毕 * 所以结果是t1线程执行完后,才到主线程执行,相当于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会 */ t1.join(); t2.start(); } } class ThreadJoinTest extends Thread { public ThreadJoinTest(String name) { super(name); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + "---" + i); } } } ~~~ 上面程序结果是先打印完小明线程,在打印小冬线程;   上面注释也大概说明了join方法的作用:在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。注意,这里调用的join方法是没有传参的,join方法其实也可以传递一个参数给它的,具体看下面的简单例子: ~~~ package com.study; public class Test01 { public static void main(String[] args) throws InterruptedException { ThreadJoinTest t1 = new ThreadJoinTest("小明"); ThreadJoinTest t2 = new ThreadJoinTest("小冬"); t1.start(); /** * join方法可以传递参数,join(10)表示main线程会等待t1线程10毫秒,10毫秒过去后, * main线程和t1线程之间执行顺序由串行执行变为普通的并行执行 */ t1.join(10); t2.start(); } } class ThreadJoinTest extends Thread { public ThreadJoinTest(String name) { super(name); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + "---" + i); } } } ~~~ 上面代码结果是:程序执行前面10毫秒内打印的都是小明线程,10毫秒后,小明和小冬程序交替打印。 所以,join方法中如果传入参数,则表示这样的意思:如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。 **join与start调用顺序问题** 上面的讨论大概知道了join的作用了,那么,入股 join在start前调用,会出现什么后果呢?先看下面的测试结果 ~~~ public class JoinTest { public static void main(String [] args) throws InterruptedException { ThreadJoinTest t1 = new ThreadJoinTest("小明"); ThreadJoinTest t2 = new ThreadJoinTest("小东"); /**join方法可以在start方法前调用时,并不能起到同步的作用 */ t1.join(); t1.start(); //Thread.yield(); t2.start(); } } class ThreadJoinTest extends Thread{ public ThreadJoinTest(String name){ super(name); } @Override public void run(){ for(int i=0;i<1000;i++){ System.out.println(this.getName() + ":" + i); } } } ~~~ 上面代码执行结果是:小明和小东线程交替打印。 所以得到以下结论:join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。 **join方法实现原理** 有了上面的例子,我们大概知道join方法的作用了,那么,join方法实现的原理是什么呢? **其实,join方法是通过调用线程的wait方法来达到同步的目的的。例如,A线程中调用了B线程的join方法,则相当于A线程调用了B线程的wait方法,在调用了B线程的wait方法后,A线程就会进入阻塞状态,具体看下面的源码**: ~~~ public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } ~~~ 从源码中可以看到:join方法的原理就是调用相应线程的wait方法进行等待操作的,例如A线程中调用了B线程的join方法,则相当于在A线程中调用了B线程的wait方法,当B线程执行完(或者到达等待时间),B线程会自动调用自身的notifyAll方法唤醒A线程,从而达到同步的目的。 # isAlive是否处于活动状态 方法isAlive()功能是判断当前线程是否处于活动状态。 活动状态就是线程启动且尚未终止,比如正在运行或准备开始运行。 ~~~ package com.study; class IsAliveThread extends Thread { public IsAliveThread() { System.out.println("begin"); System.out.println( Thread.currentThread().getName() + "- status -" + Thread.currentThread().isAlive()); System.out.println("end"); } @Override public void run() { System.out.println("run begin"); System.out.println( Thread.currentThread().getName() + "- status -" + Thread.currentThread().isAlive()); System.out.println("run end"); } } public class Test01 { public static void main(String[] args) { IsAliveThread ist = new IsAliveThread(); Thread th = new Thread(ist); System.out.println("Main begin th isAlive = " + th.isAlive()); th.start(); System.out.println("Main end th isAlive = " + th.isAlive()); } } ~~~ ## 3个线程按顺序执行 T3先执行,在T3的run中,调用t2.join,让t2执行完成后再执行t3 在T2的run中,调用t1.join,让t1执行完成后再让T2执行 ~~~ package testThread; import java.util.Date; public class TestThread { //现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行 public static void main(String[] args) { //线程1 final Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("t1"); } }); //线程2 final Thread t2 = new Thread(new Runnable() { @Override public void run() { try { //引用1线程,等待t1线程执行完 t1.join(); } catch (Exception e) { e.printStackTrace(); } System.out.println("t2"); } }); //线程3 final Thread t3 = new Thread(new Runnable() { @Override public void run() { try { //引用2线程,等待t2线程执行完 t2.join(); } catch (Exception e) { e.printStackTrace(); } System.out.println("t3"); } }); //这3个线程启动没有先后顺序 t3.start(); t2.start(); t1.start(); } } ~~~