最新多线程图解韩顺平老师2021
文章目录
- 进程线程
- 线程
- 线程基本使用
- 继承Thread创建线程
- 为什么是start类
- Runnable创建线程
- 多线程售票问题
- 线程终止 ,线程退出
- 线程中断
- 线程插队
- 守护线程
- 线程7大状态
- 线程同步机制
- 互斥锁
- 线程的死锁
- 释放锁
进程线程
- 进程
- 进程是指运行中的程序,比如我们使用qq,就启动了一个进程,操作系统就会为该进程分配内存空间。当然我们使用迅雷,又启动了一个进程,操作系统会为迅雷分配新空间。
- 进程是程序的第一次执行过程,或是正在运行的一个程序,是动态过程:有它紫色的产生,存在和消亡过程。
线程
什么是线程
- 线程由进程创建的,是进程的一个实体
- 一个进程可以拥有多个线程。
单线程和多线程
- 单线程:同一时刻,只允许执行一个线程
- 多线程:同一时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
并发和并行概念
- 并发:同一时刻,多个任务交替执行,造成一种"貌似同时"的错觉,简单的说,单核cpu实现的多任务就是并发
- 例如:1.单核cpu交替执行qq和迅雷任务 2.人开车的时候一个打电话一边开车,大脑只有一个,但是交替做两件事情
- 并发:同一时刻,多个任务交替执行,造成一种"貌似同时"的错觉,简单的说,单核cpu实现的多任务就是并发
线程基本使用
- 创建线程的两种方法(在java中线程使用两种方法)
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法
继承Thread创建线程
- 线程应用案例1-继承Thread类
package xianchen;public class Thread01 {public static void main(String[] args) {//定义Cat对象,充当线程使用Cat cat = new Cat();cat.start();//启动线程}//1.当一个类继承了Thread类,该类就可以当做线程使用//2.我们会重写run方法,写上自己的业务代码//3. run Threa类实现了Runable接口的run方法}
class Cat extends Thread {int time =0;@Overridepublic void run() {//重写run方法,写上自己的业务代码while (true) {System.out.println("喵喵,我是小猫咪"+(++time));//让线程休眠1秒try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if(time==80){break;}}}
}
调用start()线程的示意图
若调用run()方法,示意图为
//start0()是本地方法,是JVM调用,底层是c/c++实现 eat.start();//启动线程->最终会执行cat的方法run
若main线程启动子线程Thread-0,主线程不会堵塞,会继续执行
为什么是start类
- 启动线程->最终会执行cat的run方法
- 如果cat.run(),只会执行普通的run方法,不会进行真正多线程,会发生阻塞,把run方法执行完,才向下执行
Runnable创建线程
- 创建线程例子
package xianchen;public class Thread02 {public static void main(String[] args) {T2 t2 = new T2();Thread thread = new Thread(t2);thread.start();}
}class T2 implements Runnable {int times=0;@Overridepublic void run() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("h1"+(++times));if(times==80){break;}}}
}
- 创建多线程执行
请编写一个程序,创建两个线程,一个线程每个一秒输出"hello world",输出10次,退出,一个线程每隔1秒输出"hi",输出5次退出。
package xianchen;public class Thread02 {public static void main(String[] args) {T2 t2 = new T2();T3 t3 = new T3();Thread thread = new Thread(t2);Thread thread1=new Thread(t3);thread.start();thread1.start();}
}class T2 implements Runnable {int times=0;@Overridepublic void run() {while (true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("hello"+(++times));if(times==80){break;}}}
}
class T3 implements Runnable {@Overridepublic void run() {int time=0;while(true) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("你好"+(++time));if(time==50)break;}}
}
- 一个main主线程可以执行多个线程.
继承Thread和接口Runnable的对比
- 从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
- 实现Runnable接口就更加适合多线程共享一个资源的情况,并且避免了单继承的限制
多线程售票问题
- [售票系统],编写模拟三个售票窗口售票,分别使用继承Thread和实现Runnable接口,并分析有什么问题?
- 问题:会出现超卖的情况.
- 如何解决:后面会学习Synchronized,来解决
package SellTicket;public class SellTicket {public static void main(String[] args) {// SellTicket01 st1 = new SellTicket01();
// SellTicket01 st2 = new SellTicket01();
// SellTicket01 st3 = new SellTicket01();
// st1.start();
// st2.start();
// st3.start();//通过实现Runnable接口方法SellTicket02 t2 = new SellTicket02();//输出的票数,不是连续减少,而是同步和输出问题new Thread(t2).start();new Thread(t2).start();new Thread(t2).start();}
}
//方法一:售票窗口线程,继承Thread
class SellTicket01 extends Thread {static int ticket = 100;//余票@Overridepublic void run() {while (true){try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}if(ticket<=0)break;System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数"+(--ticket));}}
}
//方法二:售票窗口,用接口Runnable
class SellTicket02 implements Runnable {int ticket = 100; //余票@Overridepublic void run() {while (true){try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}if(ticket<=0)break;System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数"+(--ticket));}}}
线程终止 ,线程退出
当线程完成任务后,会自动退出
- 子线程结束后,就退出,一个自然行为
还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
- 案例:启动一个线程t,要求再main线程中去停止线程t,请编程实现
- 主线程退出不一定代表子线程也要退出
- 如想在t1线程通知t2线程退出,需要t1有控制t2的变量
package xianchenzz;public class test1 {public static void main(String[] args) throws InterruptedException {AThread t1 = new AThread();new Thread(t1).start();//如果希望main线程去控制t1线程的终止,必须可以修改loop//让t1线程退出run方法,从而终止 t1线程->通知方式//让主线程休眠10秒,在通知t1退出Thread.sleep(10*1000);t1.setLoop(false);}
}
class AThread implements Runnable {//设置一个控制变量private boolean loop =true;@Overridepublic void run() {while (loop) {try {Thread.sleep(50);//让当前线程休眠50ms} catch (InterruptedException e) {e.printStackTrace();}System.out.println("AThread运行中...");}}public void setLoop(boolean loop) {this.loop = loop;}
}
线程中断
常用方法第一组
线程常见方法
- setName //设置线程名称,使之与参数name相同
- getName //返回该线程的名称
- start //使该线程开始执行;java虚拟机底层调用该线程的start()方法
- run //调用线程对象run方法
- setPriority //更改线程的优先级
- getPriority //获取线程的优先级
- sleep //在指定的毫秒数内让当前正在执行的线程休眠
- interrupt //中断线程
下面我们测试一下线程的常用方法,看老师代码
package xianchenzd;public class ThreadMethod01 {public static void main(String[] args) throws InterruptedException {threadDemo td = new threadDemo();td.setName("小笼包");td.setPriority(Thread.MIN_PRIORITY);td.start();//测试优先级System.out.println("默认优先级"+Thread.currentThread().getPriority());//测试interruptThread.sleep(10000);td.interrupt();//中断休眠(加快程序进行)}
}
class threadDemo extends Thread { //自定义的线程类@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println(Thread.currentThread().getName() + "吃包子~~~" + i);}try {Thread.sleep(20000);} catch (InterruptedException e) {//当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码System.out.println(Thread.currentThread().getName() + "被interrupt");}}}
线程插队
常用方法第二组
yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功(看内存空间,若空间足够则不会礼让成功)
join:线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
案例:创建一个子线程,每隔1s输出hello,输出20次,主线程每隔1秒,输出hi,输出20次,要求两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程继续
package xianchen03;public class test {public static void main(String[] args) throws InterruptedException {T t = new T();t.setName("子线程");t.start();for (int i = 0; i <=20;i++) {Thread.sleep(1000);System.out.println("hi"+" "+i);if(i==5) {t.join();//join方法使子线程先进行,等子线程进行完开始主线程进行System.out.println("主线程继续!");}}}
}
class T extends Thread{@Overridepublic void run() {for (int i = 0; i <=20;i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(currentThread().getName()+"hello"+" "+i);}}
}
守护线程
常见的方法第三组
用户线程和守护线程
- 用户线程:也叫工作线程,当线程的任务执行完或通知方法结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程:垃圾回收机制
实例
- 下面我们测试如何将一个线程设置为守护线程
package xianchen04;public class test04 {public static void main(String[] args) throws InterruptedException {T4 t4 = new T4();Thread thread = new Thread(t4);thread.setDaemon(true);//设置守护线程,主线程结束,子线程也结束thread.start();for (int i = 0; i <=20;i++){Thread.sleep(1000);System.out.println("工作辛苦了"+i);}}
}
class T4 implements Runnable{@Overridepublic void run() {for(;;) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("快乐的聊天");}}
}
线程7大状态
线程的生命周期
JDK中用Thread.State枚举表示了线程的几种状态
public static enum Thread.State
extends Enum<Thread.State>
- NEW
尚未启动的线程处于此状态
- RUNNABLE
在Java虚拟机中执行的线程处于此状态。
- BLOCKED
被阻塞等待监视器锁定的线程处于此状态
- WAITING
正在等待另一个线程执行特定动作的线程处于此状态
- TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- TERMINGATED
已退出的线程处于此状态
七种状态是因为RUNNABLE状态可以分为两个状态一个是Ready,另一个是Running状态,Ready状态线程被调度器选中执行成为Running,调度器受操作系统内核控制。
写程序查看线程状态
package xianchen05;public class test05 {public static void main(String[] args) throws InterruptedException {T4 t4 = new T4();t4.setName("t4线程");System.out.println(t4.getName()+"状态"+ t4.getState());//NEW状态t4.start();while(Thread.State.TERMINATED!=t4.getState()){//当状态不为TERMINATED,则不断查看当前状态Thread.sleep(500);//sleep方法导致处于TIMED——WAITING状态System.out.println(t4.getName()+"状态"+t4.getState());}System.out.println(t4.getName()+"状态"+t4.getState());//退出的话状态为TERMINATED}
}
class T4 extends Thread{@Overridepublic 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;}}
}
线程同步机制
线程同步机制(Synchronized)
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性
- 也可以这里理解:线程同步,即当有一个线程对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才可能对该内存地址进行操作
同步具体方法-Synchronized
同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码
//需要被同步代码;
}
synchronized还可以放在方法声明中,表示整个方法为同步方法
public synchronized void m(String name){//需要被同步的代码
}
如何理解:
就好像某小伙伴上厕所前先把门关上(上锁),完事后再出来(解锁),那么其他小伙伴就可在使用厕所
使用synchronized解决售票问题
package xianchen06;public class SellTicket {public static void main(String[] args) {// 通过实现Runnable接口方法SellTicket02 t2 = new SellTicket02();
// 输出的票数,不是连续减少,而是同步和输出问题new Thread(t2).start();new Thread(t2).start();new Thread(t2).start();}
}
//方法二:售票窗口,用接口Runnable
class SellTicket02 implements Runnable {static int ticket = 100;//余票,让多个线程共享ticketprivate boolean loop =true;//控制run方法public synchronized void sell(){try {//模拟休眠50Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}if(ticket<=0) {loop = false;System.out.println("售票结束");return;}System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数"+(--ticket));}@Overridepublic void run() {while (loop) {sell();}}
}
互斥锁
分析同步原理
- t1,t2,t3同时去抢这把锁,若t1抢到,则其进行打印输出车票的操作,打印完后又锁起来,再次进行抢锁,这样就不会发生车票出现负。
互斥锁
- 基本介绍
- Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
- 每个对象都对应于一个可称为"互斥锁"的标记,这个标记用来保证在任何一个时刻,只能有一个线程访问该对象。
- 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
- 同步的局限性:导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
- 同步方法(静态的)的锁只能为当前类本身。
//1.public synchronized static void m1(){}锁是加在 SellTicket.class上//2.如果在静态方法中,实现一个同步代码快/*public static void m2(){synchronized (SellTicket.class){System.out.println("m2");}}*/
注意事项和细节
同步方法如果没有使用static修饰:默认锁对象为this
如果方法使用static修饰,默认锁对象:当前类.class
实现的落地步骤:
- 需要先分析上锁代码
- 选择同步代码块或者同步方法
- 要求多个线程的锁对象为同一个,若用对象不同,则题目争的锁不为同一把锁,就比如使用继承Thread方法
new SellTicket01().start(); new SellTicket01().start(); //这两个为不同对象
线程的死锁
- 基本介绍
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生。
- 案例分析
妈妈:你先完成作业,才让你玩手机
小明:你先让我玩手机,我才完成作业
- 例子
package xianchen06;public class DeadLock {public static void main(String[] args) {DeadLockDemo a1 = new DeadLockDemo(true);DeadLockDemo a2 = new DeadLockDemo(false);a1.start();a2.start();}
}
class DeadLockDemo extends Thread{static Object o1 = new Object(); //保证多线程,共享一个对象,这里使用staticstatic Object o2= new Object();boolean flag;public DeadLockDemo(boolean flag) {this.flag = flag;}public void run() {//1.如果为flag为 T,线程A就会先得到/持有01对象锁,然后尝试获取o2对象锁//2如果线程a1得不到02,就会Blocked//1.如果为flag为 F,线程B就会先得到/持有02对象锁,然后尝试获取o1对象锁//2如果线程B得不到01,就会Blockedif (flag) {synchronized (o1) {System.out.println(Thread.currentThread().getName()+" 进入1");synchronized (o2) { //这里获得li对象的监视权System.out.println(Thread.currentThread().getName()+" 进入2");}}}else{synchronized (o2) {System.out.println(Thread.currentThread().getName()+" 进入3");synchronized (o1) { //这里获得li对象的监视权System.out.println(Thread.currentThread().getName()+" 进入4");}}}}
}
释放锁
下面操作不会释放锁
线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,再坑位上眯了一会
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁
提示:应该尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用
下面操作会释放锁
当前线程的同步方法,同步代码块执行结束
案例:上厕所,完事出来
当前线程在同步代码块,同步方法中遇到break,return。
案例:没有正常的完事,经理叫他修改bug,不得已出来
当前线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致异常结束
案例:没有正常的完事,发现忘记带纸,不得已出来
当前线程在同步代码块,同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去。
最新多线程图解韩顺平老师2021相关推荐
- 韩顺平老师坦克大战优化版
一.项目介绍 1.前言 基于韩顺平老师坦克大战的框架和思路,进行了一些优化.编码上尽量按照阿里的代码规约:有非常详尽的注释:引入了线程池,线程安全集合类,原子类等:通过这个小项目的学习,可以深入地理解 ...
- 韩顺平老师讲解13个自学编程的坑
文章目录 前言 内容 误区一 不注重基础,什么技术火就学什么 误区二 总是纠结学最好的编程语言 误区三 喜欢看不喜欢动手 误区四 没有认识到,听懂和能使用时两回事 误区五 很少做笔记,也不去画思维导图 ...
- 韩顺平老师《一周学会Linux》视频笔记
前言: 这个教学视频使用的软件环境是: Red Hat Linux(Kernel 2.4.20-8)红帽Linux系统[release 9 shrike],在虚拟机中运行 1.成为一个Linux专家的 ...
- Java基础易忘重点内容笔记【附B站韩顺平老师课程链接】
B站课程链接:https://www.bilibili.com/video/BV1fh411y7R8?spm_id_from=333.999.0.0 1. 文档注释 用于对Java方法的注释,可据此生 ...
- Java坦克大战 跟学韩顺平老师视频开发
这里写目录标题 TankBigWarGame 介绍 界面展示 项目架构 安装教程 游戏说明 项目涉及技术功能 游戏结束判断 项目不足与优化空间 相关代码展示 主方法Main 绘图界面 MyPanelF ...
- Java集合深入剖析【韩顺平老师版】
Java集合知识体系[思维导图] 1.集合体系 1.1.为什么使用集合? 1.数组的不足 长度固定,不能更改 存储的必须是同一类型的元素(基本类型或引用类型) 增加.删除元素比较麻烦 2.集合的优势 ...
- 韩顺平老师坦克大战项目总结
韩顺平老师讲的坦克大战项目,用代码进行了复现,有几个自己的总结 1 有个别功能没有实现,EnemyTank中敌人坦克向四周移动功能没有实现,只是实现了随机转向,但一直停在原地不动,没有找到bug所在. ...
- 传智播客韩顺平老师PHP入门到精通视频免费下载
传智播客韩顺平老师PHP入门到精通视频免费下载--留下邮箱Lz发送 视频介绍: PHP,是英文超级文本预处理语言Hypertext Preprocessor的缩写.PHP 是一种 HTML 内嵌式的语 ...
- 韩顺平老师讲诉如何学习PHP
有很多网友发来邮件询问各种问题,有深有浅, 有难有易.因为很多时间需要上课,没有一一回答,这里给大家道个歉,这里我举例出了几封网友的来信: 发件人:Chen Ma 发送时间: 2012-09-18 1 ...
最新文章
- 为什么我的python没有run_为什么我的returncode=0而没有stdoutsubprocess.run?
- Guava之FluentIterable使用示例
- oracle给换服务器,Oracle数据库更换服务器10分钟切换方案
- 基于LSTM的情感分类案例:Tensorflow代码
- python中函数的括号使用
- hdu 1068(二分图最大独立集)
- Python学习笔记——glob模块【文件、路径操作】
- Lync Server 2010标准版系列PART6:启用Lync
- 现代软件工程 学生阅读和调查作业
- python中的pymysql_(转)Python中操作mysql的pymysql模块详解
- iPhone 13系列将首发A15芯片:采用增强版5nm工艺 性能提升20%
- 如何用“向上管理”搞垮一个团队?
- TensorFlow保存或加载训练的模型
- ucos任务调度函数 OSSched()函数分析 ,任务切换函数
- java hsqldb数据库,HSQLDB数据库的使用
- 电池电量显示模块、美容仪、剃须刀、血氧仪、红外体温计、脱毛器、(耳)额温枪、电子秤等段码屏LCD液晶显示驱动IC-VK1024B 6*4段显示,VK1056 14*4段显示,少脚位1621,具省电模式
- QQ自动发消息源代码
- matlab 检验异方差,stata中面板数据异方差的处理_stata面板异方差检验
- 政简网:还剩一个月时间怎么科学有效复习公务员考试?
- 一个农村小伙的淘宝创业故事
热门文章
- 强引用置为null,会不会被回收及内存分配及年轻代年老代算法回收
- java 处理物料清单_JAVA Spring MVC 物料清单BOM 展开实例
- 【毕业设计_课程设计】位置信息管理网站设计(源码+论文)
- 计算机四级维修工查询,计算机安装调试维修员(四级)技能鉴定试题单总汇.doc
- C语言中,#include的用法:#include 和 #include区别
- day01.我们为什么要学习进制
- Linux TC 带宽管理队列规则
- Gson的JsonParser使用
- Linux 配置本地域名项目
- Linux基础:文件类型