(二)Java 线程
创始人
2024-03-02 10:22:44
0

一、创建和运行线程

1. 方法一,直接使用 Thread

@Slf4j(topic = "c.Test1")
public class Demo {public static void main(String[] args) {Thread t = new Thread(){@Overridepublic void run() {log.debug("running");}};t.setName("t1");t.start();}}

2. 方法二,使用 Runnable 配合 Thread

把【线程】和【任务】(要执行的代码)分开 (1)Thread 代表线程 (2)Runnable 可运行的任务(线程要执行的代码)
@Slf4j(topic = "c.Test2")
public class Test2 {public static void main(String[] args) {Runnable r = () -> log.debug("running");Thread t = new Thread(r, "t2");t.start();}
}

3. 原理之 Thread Runnable 的关系

小结 : (1)方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了 (2)用 Runnable 更容易与线程池等高级 API 配合 (3)用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

4. 方法三,FutureTask 配合 Thread

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
@Slf4j(topic = "c.Test1")
public class Demo {public static void main(String[] args) {FutureTask task = new FutureTask<>(new Callable() {@Overridepublic Integer call() throws Exception {log.debug("running...");Thread.sleep(2000);return 100;}});Thread t1 = new Thread(task, "t1");t1.start();}
}

二、查看进程线程的方法

1. windows

任务管理器可以查看进程和线程数,也可以用来杀死进程 (1)tasklist:查看进程

(2)taskkill:杀死进程

  

2. linux

(1)ps -fe :查看所有进程

(2)ps -fT -p 查看某个进程(PID)的所有线程 (3)kill :杀死进程

(4)top 按大写 H 切换是否显示线程 (5)top -H -p 查看某个进程(PID)的所有线程

3. Java

(1)jps :命令查看所有 Java 进程 (2)jstack :查看某个 Java 进程(PID)的所有线程状态 (3)jconsole :来查看某个 Java 进程中线程的运行情况(图形界面)

4. jconsole 远程监控配置

需要以如下方式运行你的 java 类
java -Djava.rmi.server.hostname=`ip地址` -Dcom.sun.management.jmxremote -
Dcom.sun.management.jmxremote.port=`连接端口` -Dcom.sun.management.jmxremote.ssl=是否安全连接 -
Dcom.sun.management.jmxremote.authenticate=是否认证 java类

三、常见方法

1. start run

(1)直接调用 run 是在主线程中执行了 run,没有启动新的线程

(2)使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码

2. sleep yield

2.1 sleep

(1)调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)

(2)其他线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException

(3)睡眠结束后的线程未必会立刻得到执行

(4)建议用 TimeUnit 的 sleep 代替 Thread 的sleep 来获得更好的可读性

@Slf4j(topic = "c.Test8")
public class Test8 {public static void main(String[] args) throws InterruptedException {Thread.sleep(1000);TimeUnit.SECONDS.sleep(1);TimeUnit.DAYS.sleep(1);}
}

2.2 yield

(1)调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其他线程

(2)具体的实现依赖于操作系统的任务调度器

2.3 线程优先级

(1)线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它。

(2)如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲,优先级几乎没作用。

3. join 方法详解

3.1 为什么需要 join

@Slf4j(topic = "c.Test10")
public class Test10 {static int r = 0;public static void main(String[] args) throws InterruptedException {test1();}private static void test1() throws InterruptedException {log.debug("开始");Thread t1 = new Thread(() -> {log.debug("开始");sleep(1);log.debug("结束");r = 10;},"t1");t1.start();t1.join();log.debug("结果为:{}", r);log.debug("结束");}
}
分析 (1)因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10 (2)而主线程一开始就要打印 r 的结果,所以只能打印出 r=0 解决方法 (1)用 join,加在 t1.start() 之后即可

3.2 等待多个结果

3.3 有时效的 join 

 等待线程运行结束,最多等待 n 毫秒。

4. interrupt 方法详解

4.1 打断 sleep、wait、join 的线程

@Slf4j
public class Demo {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("sleep...");try {Thread.sleep(5000); // wait, join} catch (InterruptedException e) {e.printStackTrace();}},"t1");t1.start();Thread.sleep(1000);log.debug("interrupt");t1.interrupt();log.debug("打断标记:{}", t1.isInterrupted());//打断标记:false}
}

4.2 打断正常运行的线程

@Slf4j
public class Demo {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while(true) {boolean interrupted = Thread.currentThread().isInterrupted();if(interrupted) {log.debug("被打断了, 退出循环");break;}}}, "t1");t1.start();Thread.sleep(1000);log.debug("interrupt");t1.interrupt();}
}

打断正常运行的线程, Thread.currentThread().isInterrupted() 会变成 true 。

4.3 模式之两阶段终止

在一个线程 T1 中如何“优雅”终止线程 T2? 这里的【优雅】指的是给 T2 一个料理后事的机会。

错误思路:

(1)使用线程对象的 stop() 方法停止线程

        stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其他线程将永远无法获取锁。

(2)使用 System.exit(int) 方法停止线程

        目的仅是停止一个线程,但这种做法会让整个程序都停止。

 

 

@Slf4j(topic = "c.Test3")
public class Test {public static void main(String[] args) throws InterruptedException {TwoPhaseTermination tpt = new TwoPhaseTermination();tpt.start();Thread.sleep(3500);tpt.stop();}}@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{private Thread monitor;// 启动监控线程public void start(){monitor = new Thread(()->{while (true){Thread current = Thread.currentThread();if (current.isInterrupted()){log.debug("料理后事");break;}try {Thread.sleep(1000);log.debug("执行监控记录");} catch (InterruptedException e) {e.printStackTrace();// 重新设置打断标记current.interrupt();}}});monitor.start();}// 停止监控线程public void stop(){monitor.interrupt();}
}

4.4 打断 park 线程

@Slf4j(topic = "c.Test14")
public class Test14 {private static void test4() {Thread t1 = new Thread(() -> {for (int i = 0; i < 5; i++) {log.debug("park...");LockSupport.park();log.debug("打断状态:{}", Thread.interrupted());}});t1.start();sleep(1);t1.interrupt();}private static void test3() throws InterruptedException {Thread t1 = new Thread(() -> {log.debug("park...");LockSupport.park();log.debug("unpark...");log.debug("打断状态:{}", Thread.currentThread().isInterrupted());}, "t1");t1.start();sleep(1);t1.interrupt();}public static void main(String[] args) throws InterruptedException {test4();}
}

test3:如果打断标记已经是 true, 则 park 会失效

test4:可以使用 Thread.interrupted() 清除打断状态。Thread.interrupted()会重置打断标记为 false

4.5 不推荐的方法

还有一些不推荐使用的方法,这些方法已过时,容易破坏同步代码块,造成线程死锁

 

四、主线程与守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。
@Slf4j(topic = "c.Test15")
public class Test15 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {break;}}log.debug("结束");}, "t1");t1.setDaemon(true);t1.start();Thread.sleep(1000);log.debug("结束");}
}
注意 (1)垃圾回收器线程就是一种守护线程 (2)Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

五、五种状态

这是从 操作系统 层面来描述的 (1)【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联 (2)【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行 (3)【运行状态】指获取了 CPU 时间片运行中的状态 1️⃣当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换 (4)【阻塞状态】 1️⃣如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】 2️⃣等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】 3️⃣与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们 (5)【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

 

六、六种状态

这是从 Java API 层面来描述的 根据 Thread.State 枚举,分为六种状态 (1)NEW 线程刚被创建,但是还没有调用 start() 方法 (2)RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为是可运行) (3)BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分,后面会在状态转换一节详述 (4)TERMINATED 当线程代码运行结束

 

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...