【操作系统】多线程基础知识

简介: 【操作系统】多线程基础知识

思维导图:

 

1.创建线程

  1. 继承Thread父类,重写run方法。run方法里面的逻辑就是这个线程要执行的工作。创建一个MyThread实例对象不会在系统中真的创建一个线程只有调用start方法才会创建出新的线程,一直到run里的代码执行完,新的线程就会执行结束。main主线程和MyThread创建出来的新线程是宏观上并发的过程,俩边同时执行各自执行各自的
1. class MyThread extends Thread{
2. @Override
3. public void run() {
4.         System.out.println("线程MyThread的run方法");
5.     }
6. }
7. 
8. public class Demo1 {
9. 
10. public static void main(String[] args) {
11. MyThread myThread = new MyThread();
12.         myThread.start();
13.     }
14. }
  1. 实现Runnable接口,重写run方法。这里把线程要干的工作和线程本身分开了,使用Runnable专门来表示线程要完成的工作,目的是为了解耦合!
1. class MyRunnable implements Runnable{
2. @Override
3. public void run() {
4.         System.out.println("线程MyRunnable的run方法");
5.     }
6. }
7. 
8. public class Demo1 {
9. 
10. public static void main(String[] args) {
11. MyRunnable myRunnable = new MyRunnable();
12. Thread thread = new Thread(myRunnable);
13.         thread.start();
14.     }
15. }
  1. 匿名内部类创建Thread对象
1. public static void main(String[] args) {
2. Thread thread = new Thread(){
3. @Override
4. public void run() {
5.                 System.out.println("匿名内部类创建Thread对象");
6.             }
7.         };
8.         thread.start();
9.     }
  1. 匿名内部类创建Runnable对象
1. public static void main(String[] args) {
2. Thread thread = new Thread(new Runnable() {
3. @Override
4. public void run() {
5.                 System.out.println("匿名内部类创建Runnable对象");
6.             }
7.         });
8. 
9.         thread.start();
10.     }
  1. lambda表达式
1. public static void main(String[] args) {
2. Thread thread = new Thread( ()-> {
3.             System.out.println("lambda表达式创建线程");
4.         });
5.         thread.start();
6. 
7.     }

2.Thread类

  1. Thread常见的构造方法
方法 说明
Thread(); 创建线程对象
Thread(Runnable); 使用Runnable来创建
Thread(name); 创建对象并命名
Thread(Runnable,name); 使用Runnable来创建并命名

  1. Thread的常见属性。默认创建的线程是前台进程,前台进程会阻止进程退出,如果main运行完了,前台线程还没完,进程不会退出!若是后台线程,如果main等其他前台线程执行完了,这个时候即使后台线程还没有执行完,进程也会退出。当run方法执行完了,内核的线程就销毁了,但是Thread对象还在
属性 方法 说明
ID getId(); 线程的唯一标识
名称 getName(); 构造方法指定的name
状态 getState(); 线程的状态
优先级 getPriority();
是否是后台线程 isDaemon();
是否存活 isAlive(); run方法是否运行结束
是否被打断 isInterrupted(); 默认为false

  1. 获取当前线程的引用:Thread.currentThread();

3.启动线程:

  1. 调用start()才会真正的创建线程,在内核里创建PCB。
  2. start和run的区别:直接调用run并没有创建线程,只是在原来的线程中运行代码;调用start则是创建线程,在新的线程中执行代码(和原来的线程是并发的)。

4.中断线程:

  1. 线程的中断本质上仍然是让run方法尽快结束而不是run执行一半,强制结束。
  2. 方法1:自己直接定义一个标志位
1. public class Demo1 {
2. public static boolean isQuit = false;
3. public static void main(String[] args) {
4. Thread thread = new Thread(()->{
5. while(!isQuit){
6.                 System.out.println("hello thread!");
7. try {
8.                     Thread.sleep(1000);
9.                 } catch (InterruptedException e) {
10. throw new RuntimeException(e);
11.                 }
12.             }
13.             System.out.println("thread线程执行完了!");
14. 
15.         });
16.         thread.start();
17. 
18. 
19. try {
20.             Thread.sleep(1000);
21.         } catch (InterruptedException e) {
22. throw new RuntimeException(e);
23.         }
24. 
25.         isQuit = true;
26.         System.out.println("设置让threat线程结束!");
27.     }
28. }
  1. 方法二:通过interrupt()方法,使用isInterrupted来判定,使用interrupt方法来进行触发中断。如果线程是Runnable状态则设置标志位为true,如果线程是阻塞状态sleep,则会抛出异常,不设置标志位!  在主线程中,通过thread.interrupt();来中断这个线程,如果满足条件则设置标志位true。
1. public class Demo1 {
2. 
3. public static void main(String[] args) {
4. Thread thread = new Thread(()->{
5. //while(!Thread.interrupted()){
6. while(!Thread.currentThread().isInterrupted()){
7. //currentThread是Thread类的静态方法
8.                 System.out.println("hello thread!");
9. try {
10.                     Thread.sleep(1000);
11.                 } catch (InterruptedException e) {
12. // e.printStackTrace();
13. 
14. // [方式一] 立即结束线程
15. break;
16. 
17. // [方式二] 啥都不做, 不做理会. 线程继续执行
18. 
19. // [方式三] 线程稍后处理
20. // Thread.sleep(1000);
21. // break;
22.                 }
23.             }
24.             System.out.println("thread线程执行完了!");
25. 
26.         });
27.         thread.start();
28. 
29. try {
30.             Thread.sleep(1000);
31.         } catch (InterruptedException e) {
32.             e.printStackTrace();
33.         }
34. 
35.         thread.interrupt();
36.         System.out.println("设置让threat线程结束!");
37.     }
38. }

5.线程等待:

  1. 线程之间的调度顺序是不确定的,可以通过join方法来控制线程之间的结束顺序。
  2. 在main方法中调用thread.join();效果就是让main线程阻塞等待,等到thread执行完了,main才继续执行。
  3. 如果调用join之前,main线程已经结束了,此时就不需要阻塞等待了
  4. join()不带时间的版本就是有点“不见不散”的意思,带时间的版本就是类似于“过时不候”。

6.休眠线程:

  1. sleep指定休眠的时间,让线程休息一会(阻塞一会)。操作系统管理这些线程的PCB的时候是有多个链表的。调用了sleep,则这个PCB就会被移动到另外的阻塞队列中只有就绪队列才会参与调度
  2. 当sleep的时间到了,PCB就会被移动回之前的就绪队列中,移回就绪队列不代表立即就能够上CPU上执行,还需要看系统什么时候能够调度到这个线程
  3. sleep(1000),不一定真的休眠1000,一般要略多于1000

7.线程的状态:

  1. NEW:Thread对象创建出来了,但是线程还没有创建
  2. TERMINATED:内核的PCB销毁了,但是Thread对象还在
  3. RUNNABLE:就绪状态(正在CPU上运行或者在就绪队列中排队)。
  4. TIMED_WAITING:按照一定的时间进行堵塞使用.sleep()。
  5. WAITING:特殊的阻塞状态,调用wait。
  6. BLOCKED:等待锁的时候进入的阻塞状态

 

8.线程安全存在的问题:

看下面代码:正常情况下预期结果是10w,可每次的结果都不是。

1. class Counter{
2. public int count = 0;
3. public void increase(){
4.         count++;
5.     }
6. }
7. public class Demo1 {
8. private static Counter counter = new Counter();
9. private static Counter counter2 = new Counter();
10. 
11. public static void main(String[] args) throws InterruptedException {
12. Thread thread1 = new Thread(()->{
13. for (int i = 0; i < 50000; i++) {
14.                 counter.increase();
15.             }
16.         });
17. Thread thread2 = new Thread(()->{
18. for (int i = 0; i < 50000; i++) {
19.                 counter.increase();
20.             }
21.         });
22. 
23.         thread1.start();
24.         thread2.start();
25. 
26.         thread1.join();
27.         thread2.join();
28. 
29.         System.out.println("Counter:"+ counter.count);
30.     }
31. }
  1. 进行的count++操作,底层是由三条指令在CPU上完成的,第一是把内存的数据读取到CPU寄存器上->load;第二是把CPU的寄存器中的值进行+1->add;第三是把寄存器中的值写回到内存中->save
  2. 由于当前的俩个线程修改的是一个变量,由于每次修改是三个步骤(不是原子的,由于线程之间的调度顺序不确定。因此俩个线程在真正执行这些操作的时候就会有多种执行的排列顺序!
  3. 整个线程调度过程中,执行的顺序都是随机的。由于在调度过程中出现串行执行的俩种情况次数和其他情况次数不确定,因此得到的结果就是不确定的值,最终结果不会超过10w。

(更多线程安全内容尽在下期博客~)


如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

如果对您有帮助的话,

不要忘记点赞+关注哦,蟹蟹

相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
相关文章
|
15天前
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
在数字世界的底层,操作系统扮演着不可或缺的角色。它如同一位高效的管家,协调和控制着计算机硬件与软件资源。本文将拨开迷雾,深入探索操作系统中两个核心概念——进程与线程。我们将从它们的诞生谈起,逐步剖析它们的本质、区别以及如何影响我们日常使用的应用程序性能。通过简单的比喻,我们将理解这些看似抽象的概念,并学会如何在编程实践中高效利用进程与线程。准备好跟随我一起,揭开操作系统的神秘面纱,让我们的代码运行得更加流畅吧!
|
4月前
|
UED 开发者 Python
探索操作系统的心脏:理解进程与线程
【8月更文挑战第31天】在数字世界的海洋中,操作系统犹如一艘巨轮,其稳定航行依赖于精密的进程与线程机制。本文将揭开这一机制的神秘面纱,通过深入浅出的语言和直观的代码示例,引领读者从理论到实践,体验进程与线程的魅力。我们将从基础概念出发,逐步深入到它们之间的联系与区别,最后探讨如何在编程实践中高效运用这些知识。无论你是初学者还是有经验的开发者,这篇文章都将为你的技术之旅增添新的航标。
|
14天前
|
算法 调度 开发者
深入理解操作系统:进程与线程的管理
在数字世界的复杂编织中,操作系统如同一位精明的指挥家,协调着每一个音符的奏响。本篇文章将带领读者穿越操作系统的幕后,探索进程与线程管理的奥秘。从进程的诞生到线程的舞蹈,我们将一起见证这场微观世界的华丽变奏。通过深入浅出的解释和生动的比喻,本文旨在揭示操作系统如何高效地处理多任务,确保系统的稳定性和效率。让我们一起跟随代码的步伐,走进操作系统的内心世界。
|
1月前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
1月前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
44 2
|
1月前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
3月前
|
存储 消息中间件 资源调度
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
该文章总结了操作系统基础知识中的十个关键知识点,涵盖了进程与线程的概念及区别、进程间通信方式、线程同步机制、死锁现象及其预防方法、进程状态等内容,并通过具体实例帮助理解这些概念。
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
|
2月前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
40 1
|
1月前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。
|
3月前
|
资源调度 算法 调度
深入浅出操作系统之进程与线程管理
【9月更文挑战第29天】在数字世界的庞大舞台上,操作系统扮演着不可或缺的角色,它如同一位精通多门艺术的导演,精心指挥着每一个进程和线程的演出。本文将通过浅显的语言,带你走进操作系统的内心世界,探索进程和线程的管理奥秘,让你对这位幕后英雄有更深的了解。