读《java多线程核心技术》有感--第一章 java多线程技能

浅浅的花香味﹌ 2022-05-28 08:46 294阅读 0赞

本章内容:
这里写图片描述

关键技术:
1 线程的启动
2 如何让线程暂停
3 如何使线程停止
4 线程的优先级
5 线程的安全问题


1.1 进程和多线程的概念及线程优点

简单地说:一个正在运行的应用程序就是一个进程,比如任务管理器里面的每一个进程。

这里写图片描述

然后说线程:线程就是进程在任务进行中的子任务,比如QQ.exe里面的视频线程,下载文件线程,表情包线程。

也就是说将进程拆分成多个线程,这样的好处是可以合理的尽可能的使用CPU资源,同时CPU切换任务很快,给我们的感觉多任务同时进行,用户体验好。

1.2 使用多线程

下面看一个代码:
这里写图片描述

主线程main是JVM创建的,我要说的是main()与线程名称毫无关系,只是名字相同而已。


1.2.1 继承Thread类

具体代码:

  1. package com.wuk.demo;
  2. public class Test02 extends Thread{
  3. @Override
  4. public void run() {
  5. System.out.println("正在学习多线程");
  6. }
  7. public static void main(String[] args) {
  8. Test02 test02=new Test02();
  9. test02.start();
  10. System.out.println("结束");
  11. }
  12. }

结果:

  1. 结束
  2. 正在学习多线程

从这里可以看出,线程调用的随机性。

下面再演示一下线程的随机性。

  1. package com.wuk.demo;
  2. public class Test03 extends Thread{
  3. @Override
  4. public void run() {
  5. for(int i=0;i<5;i++){
  6. int time=(int) (Math.random()*1000);
  7. try {
  8. Thread.sleep(time);
  9. System.out.println("run="+Thread.currentThread().getName());
  10. } catch (InterruptedException e) {
  11. // TODO Auto-generated catch block
  12. e.printStackTrace();
  13. }
  14. }
  15. }
  16. public static void main(String[] args) {
  17. Test03 test03=new Test03();
  18. //设置线程名字
  19. test03.setName("test03");
  20. test03.start();
  21. for(int i=0;i<5;i++){
  22. int time=(int) (Math.random()*1000);
  23. try {
  24. Thread.sleep(time);
  25. System.out.println("main="+Thread.currentThread().getName());
  26. } catch (InterruptedException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. }

结果如下:

  1. run=test03
  2. main=main
  3. run=test03
  4. main=main
  5. run=test03
  6. run=test03
  7. main=main
  8. run=test03
  9. main=main
  10. main=main

start()方法就是通知线程规划器,该线程已经准备好,等待调用run()方法,其实就是让系统安排一个时间来调用该对象的run()方法,如果直接调用run()方法,就不会涉及到线程规划器了,就是同步而非异步。
还要注意,调用start()的顺序不是线程运行的顺序,具体的调度顺序是根据cpu分配的时间片段。


1.2.2实现Runnable()接口

具体代码如下:

  1. package com.wuk.demo;
  2. public class Test03 implements Runnable{
  3. @Override
  4. public void run() {
  5. System.out.println("学习多线程");
  6. }
  7. public static void main(String[] args) {
  8. Test03 test03=new Test03();
  9. new Thread(test03).start();
  10. System.out.println("结束");
  11. }
  12. }

结果:

  1. 结束
  2. 学习多线程

注意看和继承Thread的线程启动的区别,实现Runnable接口的启动方式还是要借助Thread对象。

看一下Thread的源码:
这里写图片描述

Thread 实现了Runnable接口 这样的话我们可以把一个线程的run()方法交给另一个线程执行。


1.2.3 实例变量与线程

资源共享问题

看具体代码:
代码1: 资源不共享

  1. package com.wuk.demo;
  2. public class Test05 extends Thread {
  3. private int count = 10;
  4. public Test05(String name) {
  5. // 线程名称
  6. this.setName(name);
  7. }
  8. @Override
  9. public void run() {
  10. for (int i = 1; i <= count; count--) {
  11. System.out.println(Thread.currentThread().getName() + "电影院卖出第"
  12. + count + "张票");
  13. }
  14. }
  15. public static void main(String[] args) {
  16. Test05 test0501 = new Test05("A");
  17. Test05 test0502 = new Test05("B");
  18. Test05 test0503 = new Test05("C");
  19. test0501.start();
  20. test0502.start();
  21. test0503.start();
  22. }
  23. }

结果:

  1. B电影院卖出第10张票
  2. A电影院卖出第10张票
  3. C电影院卖出第10张票
  4. A电影院卖出第9张票
  5. B电影院卖出第9张票
  6. B电影院卖出第8张票
  7. B电影院卖出第7张票
  8. A电影院卖出第8张票
  9. C电影院卖出第9张票
  10. A电影院卖出第7张票
  11. B电影院卖出第6张票
  12. B电影院卖出第5张票
  13. A电影院卖出第6张票
  14. C电影院卖出第8张票
  15. A电影院卖出第5张票
  16. B电影院卖出第4张票
  17. A电影院卖出第4张票
  18. C电影院卖出第7张票
  19. A电影院卖出第3张票
  20. B电影院卖出第3张票
  21. B电影院卖出第2张票
  22. A电影院卖出第2张票
  23. C电影院卖出第6张票
  24. A电影院卖出第1张票
  25. B电影院卖出第1张票
  26. C电影院卖出第5张票
  27. C电影院卖出第4张票
  28. C电影院卖出第3张票
  29. C电影院卖出第2张票
  30. C电影院卖出第1张票

代码2 :资源共享:

  1. package com.wuk.demo;
  2. public class Test05 extends Thread {
  3. private static int count = 10;
  4. @Override
  5. public void run() {
  6. for (int i = 1; i <= count; count--) {
  7. System.out.println(Thread.currentThread().getName() + "电影院卖出第"
  8. + count + "张票");
  9. }
  10. }
  11. public static void main(String[] args) {
  12. Test05 test05 = new Test05();
  13. Thread t1=new Thread(test05,"A");
  14. Thread t2=new Thread(test05,"B");
  15. Thread t3=new Thread(test05,"C");
  16. t1.start();
  17. t2.start();
  18. t3.start();
  19. }
  20. }
  21. A电影院卖出第10张票
  22. B电影院卖出第10张票
  23. C电影院卖出第10张票
  24. B电影院卖出第8张票
  25. A电影院卖出第9张票
  26. B电影院卖出第6张票
  27. C电影院卖出第7张票
  28. B电影院卖出第4张票
  29. A电影院卖出第5张票
  30. A电影院卖出第1张票
  31. B电影院卖出第2张票
  32. C电影院卖出第3张票

这里之所以出现同时卖第十张票的原因是出现并发问题,但是这里很显然达到了资源共享。

线程安全问题,

  1. package com.wuk.demo;
  2. public class Test05 extends Thread {
  3. private static int count = 3;
  4. @Override
  5. public void run() {
  6. if(count<=0){
  7. System.out.println(Thread.currentThread().getName()+"无票");
  8. return;
  9. }
  10. System.out.println(Thread.currentThread().getName() + "电影院卖出第"
  11. + count-- + "张票");
  12. }
  13. public static void main(String[] args) {
  14. Test05 test05 = new Test05();
  15. Thread t1=new Thread(test05,"A");
  16. Thread t2=new Thread(test05,"B");
  17. Thread t3=new Thread(test05,"C");
  18. Thread t4=new Thread(test05,"D");
  19. Thread t5=new Thread(test05,"E");
  20. t1.start();
  21. t2.start();
  22. t3.start();
  23. t4.start();
  24. t5.start();
  25. }
  26. }
  27. A电影院卖出第3张票
  28. B电影院卖出第3张票
  29. D电影院卖出第2张票
  30. E无票
  31. C电影院卖出第1张票

AB电影院出现了线程安全问题,在相互不知情的情况下同时卖出第3张票。
处理办法如下:
在run()前面加上synchronized,这样保持一个线程执行的时候,其他线程在外面等候,结果如下:

  1. A电影院卖出第3张票
  2. C电影院卖出第2张票
  3. D电影院卖出第1张票
  4. B无票
  5. E无票

1.3 currentThread()方法

currentThread()方法返回代码段正在被哪个线程调用的信息。
比如:

  1. Thread.currentThread().getName()

1.4 isAlive()方法

isAlive()判断当前的线程是否处于活动状态。
活动状态:指的是线程已经启动且尚未终止,处于正在运行或者准备开始运行的状态。

  1. package com.wuk.demo;
  2. public class Test07 extends Thread {
  3. @Override
  4. public void run() {
  5. System.out.println("线程状态="+Thread.currentThread().isAlive());
  6. }
  7. public static void main(String[] args) {
  8. Test07 test07=new Test07();
  9. System.out.println("begin---"+test07.isAlive());
  10. test07.start();
  11. System.out.println("end---"+test07.isAlive());
  12. }
  13. }

结果:

  1. begin---false
  2. end---true
  3. 线程状态=true

注意:end打印出来的结果也是true,但是该值不确定,因为是该线程尚未结束,所以输出true。

  1. package com.wuk.demo;
  2. public class Test07 extends Thread {
  3. @Override
  4. public void run() {
  5. System.out.println("线程状态="+Thread.currentThread().isAlive());
  6. }
  7. public static void main(String[] args) {
  8. Test07 test07=new Test07();
  9. System.out.println("begin---"+test07.isAlive());
  10. test07.start();
  11. try {
  12. Thread.sleep(1000);
  13. } catch (InterruptedException e) {
  14. // TODO Auto-generated catch block
  15. e.printStackTrace();
  16. }
  17. System.out.println("end---"+test07.isAlive());
  18. }
  19. }

结果:

  1. begin---false
  2. 线程状态=true
  3. end---false

因为在一秒之前线程结束了。


1.5 sleep()方法

sleep()让正在执行的线程(this.currentThread())休眠,暂停线程。


1.6 getId()方法

getId()获得线程的唯一标识。


1.7 停止线程

停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是说放弃当前操作。
停止的三种方法:
(1)使用退出标志,使得线程正常退出,也就是run()方法执行完成后线程终止。
(2)使用stop()终止线程,不推荐。
(3)使用interrupt()中断线程。


1.7.1 停不了的线程

案例:
使用interrupt()方法停止线程,但是他的效果并不会像break那样直接停止了。它仅仅只是给线程打了一个停止的标志,并不是真正停止了。


判断线程是否是停止状态

发表评论

表情:
评论列表 (有 0 条评论,294人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java线技能

    多线程是异步的,所以千万不要把Eclipse里代码的顺序当成线程执行的顺序,线程被调用的时机是随机的。 使用多线程 继承Thread类 使用多线程技术时,代

    相关 Java线技能

     下面进入Java多线程的学习,首先介绍Thread类的核心方法 \   线程的启动 \   线程的暂停    \   线程停止 \   线程的优先级 \   线程安