当前位置:网站首页>多线程重点知识归纳总结。
多线程重点知识归纳总结。
2022-06-09 06:59:00 【小黎的培培笔录】
目录
一、线程相关概念
1、程序
程序是一个指令序列。是为完成特定任务、 用某种语言编写的一组指令的集合。简单的说:就是我们写的代码
它以某些程序设计语言编写,运行于某种目标结构体系上。打个比方,程序就如同以英语(程序设计语言)写作的文章,要让一个懂得英语的人(编译器)同时也会阅读这篇文章的人(结构体系)来阅读、理解、标记这篇文章。一般的,以英语文本为基础的计算机程序要经过编译、链接而成为人难以解读,但可轻易被计算机所解读的数字格式,然后放入运行。
2、进程
进程是指运行中的程序, 比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。 当我们使用迅雷,又启动了一个进程,操作系统将为迅雷分配新的内存空间。
进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程 : 有它自身的产生 存在和消亡的过程3、线程
(1)基本介绍
线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程是独立调度和分派的基本单位。线程可以为操作系统内核调度的内核线程,如Win32线程;由用户进程自行调度的用户线程,如Linux平台的POSIX Thread;或者由内核与用户进程,如Windows 7的线程,进行混合调度。
一个进程可以有很多线程,每条线程并行执行不同。
同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境(register context),自己的线程本地存储(thread-local storage)。
(2)单线程
同一个时刻, 只允许执行一个线程。(单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。)
(3)多线程
同一个时刻, 可以执行多个线程,比如 : 一个qq进程, 可以同时打开多个天窗口,一个迅需进程, 可以同时下载多个文件
(4)并发
同一个时刻,多个任务交替执行,造成一种“貌似同时” 的错觉, 简单的说, 单核cpu实现的多任务就是井发。
(5)并行
同一个时刻,多个任务同时执行。多核cpu可以实现并行。
2、多线程应用
1、创建线程的两种方式
(1)继承 Thread 类,重写 run 方法
(2)实现 Runnable 接口,重写 run 方法
(3)Thread 类示意图:
2、代码示例
当一个类继承了 Thread 类, 该类就可以当做线程使用我们会重写 run 方法,写上自己的业务代码public class Thread01 { public static void main(String[] args) throws InterruptedException{//抛出异常 //创建一个Cat对象,当线程使用 Cat cat = new Cat(); //启动线程 cat.start(); //当主线程(main)启动一个子线程(Thread-0) ,主线程不会阻塞(即停止),会继续执行 //这时主线程和子线程是交替执行的(交替速度极快) //Thread.currentThread().getName() 获取线程的名称 System.out.println("主线程继续执行" + Thread.currentThread().getName()); for (int i = 0; i < 60; i++){ System.out.println("主线程 i=" + i); //让主线程休眠 Thread.sleep(1000); } } } //当一个类继承了 Thread类,该类就可以当做线程使用 //我们会重写run方法,写上自己的业务代码 class Cat extends Thread{ //记录次数,为了控制退出线程 int times = 0; @Override public void run() { while (true) { //该线程每隔一秒,在控制台输出子线程 System.out.println("子线程 :" + (++times) + "线程名=" + Thread.currentThread().getName()); //让该线程休眠一秒 //此处有异常,需要try-catch try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //退出线程 if (times == 80){ break;//当times到80时,线程就退出了 } } } }3、底层
三、Runnable 接口
1、为什么要实现接口
- java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时再用继承Thread类方法来创建线程显然不可能了。
- java设计者们提供了另外一个方式创建线程,就是通过实现 Runnable 接口来创建线程
2、代码示例:如何使用
public class Runnable01 { public static void main(String[] args) { Text text = new Text(); //一定要注意:这里不能直接调用start() //需要创建Thread对象,把text对象(实现Runnable)放入到Thread Thread thread = new Thread(text); //然后再调用 start()方法 thread.start(); } } // Text类 实现 Runnable 接口 class Text implements Runnable { //计数 int count = 0; @Override public void run() {//普通方法 while (true) { System.out.println("hi" + (++count) + Thread.currentThread().getName()); try {//休眠1秒,有异常进行try-catch Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //退出线程 if (count == 10) { break; } } } }3、静态代理模式:模拟极简的Tread类
public class Runnable01 { public static void main(String[] args) { //创建T1对象 T1 t1 = new T1(); //创建ThreadProxy 对象,参数是t1对象 ThreadProxy threadProxy = new ThreadProxy(t1); //启动线程,调用start()方法 threadProxy.start(); } } class T { } class T1 extends T implements Runnable { @Override public void run() { System.out.println("T1类"); } } //静态代理模式 //模拟了一个极简的Thread类 class ThreadProxy implements Runnable { //属性,类型是 Runnable private Runnable target = null; //构造器 public ThreadProxy(Runnable target) { this.target = target; } //start()方法 public void start() { //调用start0()方法 start0(); } //start0()方法 public void start0() { //调用run方法 run(); } @Override public void run() { if (target != null) { target.run(); //动态绑定(运行类型T1) } } }运行顺序:
ThreadProxy对象的start()方法 --> start0()方法 --> ThreadProxy对象的run() 方法 --> 创建ThreadProxy对象时,传入了t1对象,所以target 不为空 --> 运行类型是传入是t1对象,所以会调用 T1类 的 run()方法
4、继承Tread类 和 实现Runnable接口的区别
- 从java的设计来看, 通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk文档中我们可以知道Thread类本身就实现了Runnable接口
- 实现Runnable接口方式更加适合多个线程共享一个资源的情况 并且避免了单继承的限制,建议使用Runnable
四、线程常用方法
1、补充-线程终止
- 线程终止就是当线程完成任务后,会自动退出。
- 还可以通过使用变量来控制 run方法 退出的方式停止线程,即通知方式
- 代码示例
public class Exit_ { public static void main(String[] args) throws InterruptedException{ T t = new T(); t.start(); //通知方式 //让主线程休眠10秒 Thread.sleep(10 * 1000); //如果希望main线程去控制t 线程的终止,只需要修改loop //修改loop的值为false 让t退出run方法,而终止t线程 t.setLoop(false); } } class T extends Thread{ //计数 int count = 0; //设置一个控制变量,控制线程的退出 private boolean loop = true; @Override public void run() { while (loop){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程正在运行...." + (++count)); } } //用来修改 loop 属性 public void setLoop(boolean loop){ this.loop = loop; } }2、常用方法
方法名 功能 setName 设置线程称 getName 返回该线程的名称,使之与参数name相同 start 使该线程开始执行,即启动线程,Java 虚拟机底层调用该线程的start0方法。注意:底层会创建新的线程,调用run方法,run方法就是一个简单的方法调用,不会启动新线程 run 调用线程对象 run方法 setPriority 更改线程的优先级 getPriority 获取线程的优先级 sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),(线程的静态方法) interrupt 中断线程。注意中断线程, 是没有真正的结束线程的。所以一般用于中断正在休眠线程 yield 线程的礼让。 让出cpu, 让其他线程执行,但礼让的时间不确定 所以也不一定礼让成功 join 线程的插队。 插队的线程一旦插队成功,则肯定先执行完插入的线程的所有任务,然后再依次执行其他任务 join :线程插队代码示例
public class Method02 { public static void main(String[] args) throws InterruptedException{ T2 t2 = new T2(); t2.start(); for (int i = 1; i <= 20; i++) { Thread.sleep(1000); System.out.println("主线程执行...." + i); if (i == 5){ System.out.println("主线程让子线程先执行"); //join --> 线程插队 t2.join(); System.out.println("子线程执行完,主线程继续执行"); } } } } class T2 extends Thread{ @Override public void run() { for(int i = 1;i <= 20; i++){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程在执行...." + i); } } }解释:主线程和子线程会先交替执行,当 i = 5 时子线程会线程插队,然后只执行子线程,当子线程执行完毕,再继续执行主线程。
3、守护线程
- 用户线程 : 也叫工作线程,当线程的任务执行完毕或通知的方式结束
- 守护线程 : 一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程 : 垃圾回收机制
- 代码示例:
public class Method03 { public static void main(String[] args) throws InterruptedException{ MyDaemonThread myDaemonThread = new MyDaemonThread(); //如果我们希望主线程结束后,子线程自动结束,只需将子线程设置成守护线程即可 myDaemonThread.setDaemon(true); myDaemonThread.start(); for (int i = 1; i <= 10; i++) { System.out.println("主线程在执行...."); Thread.sleep(1000); } } } class MyDaemonThread extends Thread{ @Override public void run() { //无限循环 for (; ;) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("子线程在执行...."); } } }关键点:在启动线程之前,把 setDaemon()方法设置成 true
五、线程的生命周期
1、线程的几种状态
2、线程状态转换图
3、代码示例:查看线程状态
public class State { public static void main(String[] args) throws InterruptedException{ T t = new T(); //查看初始状态,调用方法getState() System.out.println(t.getName() + " 状态" + t.getState()); //启动线程 t.start(); //循环查看线程的状态 while (Thread.State.TERMINATED != t.getState()){ System.out.println(t.getName() + " 状态" + t.getState()); Thread.sleep(500); } System.out.println(t.getName() + " 状态" + t.getState()); } } class T extends Thread{ @Override public void run() { while (true){ for (int i = 0; i < 10; i++) { System.out.println("hi " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } break; } } }
六、线程同步机制
1、基本介绍
- 在多线程编程时,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
- 也可以这样理解 : 线程同步, 即当有一个线程在对内存进行操作时, 其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作
2、synchronized 关键字
(1)同步代码块
synchronized (对象){//得到对象的锁,才能操作同步代码//需要被同步代码
}
(2)同步方法public synchronized void m1(String name){
//需要被同步的代码
}
3、互斥锁
(1)基本介绍
- Java语言中,引入了对象互斤锁的概念, 来保证共享数据操作的完整性。
- 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
- 关键字synchronized 来与对象的互斥锁联系。 当某个对象用 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问。
- 同步的局限性 : 导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this, 也可以是其他对象(要求是同一个对象)
- 同步方法 (静态的)的锁为当前类本身。
(2)注意事项
- 同步方法如果没有使用static修饰 : 默认锁对象为this
- 如果方法使用static修饰, 默认锁对象 :当前类.class
- 实现的落地步骤 : ①需要先分析上锁的代码 ②选择同步代码块或同步方法 ③要求多个线程的锁对象为同一个即可!
(3)代码示例
public class SellTicket { public static void main(String[] args) { SellTicket03 sellTicket03 = new SellTicket03(); new Thread(sellTicket03).start(); new Thread(sellTicket03).start(); new Thread(sellTicket03).start(); } } //使用Runnable方式 class SellTicket03 implements Runnable{ //控制线程退出 boolean loop = true; private static int ticketNum = 100; //让多个线程共享ticketnum //在同一时刻只能有一个线程来执行sell方法,加synchronized关键字 public synchronized void sell(){ if (ticketNum <= 0){ System.out.println("售票结束。。。"); loop=false; return; } System.out.println("剩余票数" + (--ticketNum)); } @Override public void run() { while (loop){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //调用方法,实现售票 sell(); } } }4、线程死锁
- 多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生。
5、释放锁
- 当前线程的同步方法、 同步代码块执行结束。就会释放锁
- 当前线程在同步代码块、 同步方法中遇到break,return。就会释放锁
- 当前线程在同步代码块、同步方法中出现了未处理的Error 或 Exception,导致异常结束。就会释放锁
- 当前线程在同步代码块、 同步方法中执行了线程对象的 wait()方法, 当前线程暂停,并释放锁
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方
法暂停当前线程的执行,不会释放锁- 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,
该线程不会释放锁- 注意:应尽量避免使用suspend()和resume()来控制线程, 方法不再推荐使用
边栏推荐
- Chapter_ 02 how to scan and view images, query tables and time metrics in opencv
- UML series articles (21) high level behavior - events and signals
- PPT导入视频裁剪后,如何裁剪后的视频另存为保存下来?
- 政务大厅导航系统功能解读
- Matlab: difference between tf2zp and tf2zpk
- 多线程并发-私有构造函数捕获模式
- MongoDB出现Error parsing command line: unrecognised option ‘--fork‘ 的解决方法
- Fastadmin custom exported excl table name + time
- Chapter_ 01 mat: basic image container
- parker液压马达要注意哪些问题?
猜你喜欢

The original, skillful and vulgar skills of cloud
![[C language] summary of document knowledge points](/img/d3/dd5fae70bf7b15280ec32d50aca91b.jpg)
[C language] summary of document knowledge points

How to store the MOOG servo valve? Simple methods

10. regular expression matching

Idea如何查看保存代码的路径

市场变化,欢聚集团如何穿越不确定性风暴?

Performance comparison of raspberry pie Jetson nano k210 openmv

Mendeley and other document management tools to insert references in word

257. 二叉树的所有路径

Error (119013): Current license file does not support the EP4CE10F17C8 device解决方法
随机推荐
UML series article (22) advanced behavior -- state machine
市场变化,欢聚集团如何穿越不确定性风暴?
For an experienced software engineer, what would be a preferred new programming language to learn?
UML series articles (19) basic behavior - interaction diagram
Mongodb basic understanding
fastadmin 自定义导出的excl表名称+时间
工控安全行为规范
新职业:数字化管理师太抢手,只要来面试,企业就会发offer
生产者消费者问题
对服装纺织行业的认知
蘑菇街發布2022財年財報:下半年虧損同比收窄50%
Error parsing command line: unrecognized option '--fork' in mongodb
Mendeley 等文献管理工具在word中插入参考文献的报错解决
Do you really understand the logic that two objects are equal in dart?
UML series articles (21) high level behavior - events and signals
Application and analysis of special cases of data visualization
Idea如何查看保存代码的路径
Fourier transform signal processing
Quit smoking log_ 01 (day_02)
分布式消息中间件--消息队列介绍



