文章目录

  • 进程线程
  • 线程
  • 线程基本使用
  • 继承Thread创建线程
  • 为什么是start类
  • Runnable创建线程
  • 多线程售票问题
  • 线程终止 ,线程退出
  • 线程中断
  • 线程插队
  • 守护线程
  • 线程7大状态
  • 线程同步机制
  • 互斥锁
  • 线程的死锁
  • 释放锁

进程线程

  1. 进程

    • 进程是指运行中的程序,比如我们使用qq,就启动了一个进程,操作系统就会为该进程分配内存空间。当然我们使用迅雷,又启动了一个进程,操作系统会为迅雷分配新空间。
    • 进程是程序的第一次执行过程,或是正在运行的一个程序,是动态过程:有它紫色的产生,存在和消亡过程。

线程

  1. 什么是线程

    • 线程由进程创建的,是进程的一个实体
    • 一个进程可以拥有多个线程。
  2. 单线程和多线程

    • 单线程:同一时刻,只允许执行一个线程
    • 多线程:同一时刻,可以执行多个线程,比如:一个qq进程,可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件
  3. 并发和并行概念

    • 并发:同一时刻,多个任务交替执行,造成一种"貌似同时"的错觉,简单的说,单核cpu实现的多任务就是并发

      • 例如:1.单核cpu交替执行qq和迅雷任务 2.人开车的时候一个打电话一边开车,大脑只有一个,但是交替做两件事情

线程基本使用

  1. 创建线程的两种方法(在java中线程使用两种方法)

    • 继承Thread类,重写run方法
    • 实现Runnable接口,重写run方法

继承Thread创建线程

  1. 线程应用案例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;}}}
}
  1. 调用start()线程的示意图

  2. 若调用run()方法,示意图为

  3. //start0()是本地方法,是JVM调用,底层是c/c++实现
    eat.start();//启动线程->最终会执行cat的方法run
    
  4. 若main线程启动子线程Thread-0,主线程不会堵塞,会继续执行


为什么是start类

  1. 启动线程->最终会执行cat的run方法
  2. 如果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的对比

  1. 从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
  2. 实现Runnable接口就更加适合多线程共享一个资源的情况,并且避免了单继承的限制

多线程售票问题

  1. [售票系统],编写模拟三个售票窗口售票,分别使用继承Thread和实现Runnable接口,并分析有什么问题?
  2. 问题:会出现超卖的情况.
  3. 如何解决:后面会学习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));}}}

线程终止 ,线程退出

  1. 当线程完成任务后,会自动退出

    • 子线程结束后,就退出,一个自然行为
  2. 还可以通过使用变量来控制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;}
}

线程中断

常用方法第一组

  1. 线程常见方法

    • setName //设置线程名称,使之与参数name相同
    • getName //返回该线程的名称
    • start //使该线程开始执行;java虚拟机底层调用该线程的start()方法
    • run //调用线程对象run方法
    • setPriority //更改线程的优先级
    • getPriority //获取线程的优先级
    • sleep //在指定的毫秒数内让当前正在执行的线程休眠
    • interrupt //中断线程
  2. 下面我们测试一下线程的常用方法,看老师代码

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");}}}

线程插队

常用方法第二组

  1. yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功(看内存空间,若空间足够则不会礼让成功)

  2. join:线程的插队,插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

  3. 案例:创建一个子线程,每隔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);}}
}

守护线程

常见的方法第三组

  1. 用户线程和守护线程

    • 用户线程:也叫工作线程,当线程的任务执行完或通知方法结束
    • 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
    • 常见的守护线程:垃圾回收机制
  2. 实例

    • 下面我们测试如何将一个线程设置为守护线程
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大状态

线程的生命周期

  1. JDK中用Thread.State枚举表示了线程的几种状态

    public static enum Thread.State

    extends Enum<Thread.State>

    • NEW

    尚未启动的线程处于此状态

    • RUNNABLE

    在Java虚拟机中执行的线程处于此状态。

    • BLOCKED

    被阻塞等待监视器锁定的线程处于此状态

    • WAITING

    正在等待另一个线程执行特定动作的线程处于此状态

    • TIMED_WAITING

    正在等待另一个线程执行动作达到指定等待时间的线程处于此状态

    • TERMINGATED

    已退出的线程处于此状态

  2. 七种状态是因为RUNNABLE状态可以分为两个状态一个是Ready,另一个是Running状态,Ready状态线程被调度器选中执行成为Running,调度器受操作系统内核控制。

  3. 写程序查看线程状态

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)

    1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性
    2. 也可以这里理解:线程同步,即当有一个线程对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才可能对该内存地址进行操作
  • 同步具体方法-Synchronized

    1. 同步代码块

      synchronized(对象){//得到对象的锁,才能操作同步代码

      //需要被同步代码;

      }

    2. synchronized还可以放在方法声明中,表示整个方法为同步方法

  public synchronized void m(String name){//需要被同步的代码

}

  1. 如何理解:

    就好像某小伙伴上厕所前先把门关上(上锁),完事后再出来(解锁),那么其他小伙伴就可在使用厕所

  2. 使用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();}}
}

互斥锁

分析同步原理

  1. t1,t2,t3同时去抢这把锁,若t1抢到,则其进行打印输出车票的操作,打印完后又锁起来,再次进行抢锁,这样就不会发生车票出现负。

互斥锁

  • 基本介绍

    1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
    2. 每个对象都对应于一个可称为"互斥锁"的标记,这个标记用来保证在任何一个时刻,只能有一个线程访问该对象。
    3. 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问。
    4. 同步的局限性:导致程序的执行效率要降低
    5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
    6. 同步方法(静态的)的锁只能为当前类本身。
//1.public synchronized static void m1(){}锁是加在 SellTicket.class上//2.如果在静态方法中,实现一个同步代码快/*public  static  void  m2(){synchronized (SellTicket.class){System.out.println("m2");}}*/
  • 注意事项和细节

    1. 同步方法如果没有使用static修饰:默认锁对象为this

    2. 如果方法使用static修饰,默认锁对象:当前类.class

    3. 实现的落地步骤:

      • 需要先分析上锁代码
      • 选择同步代码块或者同步方法
      • 要求多个线程的锁对象为同一个,若用对象不同,则题目争的锁不为同一把锁,就比如使用继承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");}}}}
}

释放锁

  • 下面操作不会释放锁

    1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁

      案例:上厕所,太困了,再坑位上眯了一会

    2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁

    ​ 提示:应该尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用

  • 下面操作会释放锁

    1. 当前线程的同步方法,同步代码块执行结束

      案例:上厕所,完事出来

    2. 当前线程在同步代码块,同步方法中遇到break,return。

      案例:没有正常的完事,经理叫他修改bug,不得已出来

    3. 当前线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致异常结束

      案例:没有正常的完事,发现忘记带纸,不得已出来

    4. 当前线程在同步代码块,同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

      案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去。

最新多线程图解韩顺平老师2021相关推荐

  1. 韩顺平老师坦克大战优化版

    一.项目介绍 1.前言 基于韩顺平老师坦克大战的框架和思路,进行了一些优化.编码上尽量按照阿里的代码规约:有非常详尽的注释:引入了线程池,线程安全集合类,原子类等:通过这个小项目的学习,可以深入地理解 ...

  2. 韩顺平老师讲解13个自学编程的坑

    文章目录 前言 内容 误区一 不注重基础,什么技术火就学什么 误区二 总是纠结学最好的编程语言 误区三 喜欢看不喜欢动手 误区四 没有认识到,听懂和能使用时两回事 误区五 很少做笔记,也不去画思维导图 ...

  3. 韩顺平老师《一周学会Linux》视频笔记

    前言: 这个教学视频使用的软件环境是: Red Hat Linux(Kernel 2.4.20-8)红帽Linux系统[release 9 shrike],在虚拟机中运行 1.成为一个Linux专家的 ...

  4. Java基础易忘重点内容笔记【附B站韩顺平老师课程链接】

    B站课程链接:https://www.bilibili.com/video/BV1fh411y7R8?spm_id_from=333.999.0.0 1. 文档注释 用于对Java方法的注释,可据此生 ...

  5. Java坦克大战 跟学韩顺平老师视频开发

    这里写目录标题 TankBigWarGame 介绍 界面展示 项目架构 安装教程 游戏说明 项目涉及技术功能 游戏结束判断 项目不足与优化空间 相关代码展示 主方法Main 绘图界面 MyPanelF ...

  6. Java集合深入剖析【韩顺平老师版】

    Java集合知识体系[思维导图] 1.集合体系 1.1.为什么使用集合? 1.数组的不足 长度固定,不能更改 存储的必须是同一类型的元素(基本类型或引用类型) 增加.删除元素比较麻烦 2.集合的优势 ...

  7. 韩顺平老师坦克大战项目总结

    韩顺平老师讲的坦克大战项目,用代码进行了复现,有几个自己的总结 1 有个别功能没有实现,EnemyTank中敌人坦克向四周移动功能没有实现,只是实现了随机转向,但一直停在原地不动,没有找到bug所在. ...

  8. 传智播客韩顺平老师PHP入门到精通视频免费下载

    传智播客韩顺平老师PHP入门到精通视频免费下载--留下邮箱Lz发送 视频介绍: PHP,是英文超级文本预处理语言Hypertext Preprocessor的缩写.PHP 是一种 HTML 内嵌式的语 ...

  9. 韩顺平老师讲诉如何学习PHP

    有很多网友发来邮件询问各种问题,有深有浅, 有难有易.因为很多时间需要上课,没有一一回答,这里给大家道个歉,这里我举例出了几封网友的来信: 发件人:Chen Ma 发送时间: 2012-09-18 1 ...

最新文章

  1. 为什么我的python没有run_为什么我的returncode=0而没有stdoutsubprocess.run?
  2. Guava之FluentIterable使用示例
  3. oracle给换服务器,Oracle数据库更换服务器10分钟切换方案
  4. 基于LSTM的情感分类案例:Tensorflow代码
  5. python中函数的括号使用
  6. hdu 1068(二分图最大独立集)
  7. Python学习笔记——glob模块【文件、路径操作】
  8. Lync Server 2010标准版系列PART6:启用Lync
  9. 现代软件工程 学生阅读和调查作业
  10. python中的pymysql_(转)Python中操作mysql的pymysql模块详解
  11. iPhone 13系列将首发A15芯片:采用增强版5nm工艺 性能提升20%
  12. 如何用“向上管理”搞垮一个团队?
  13. TensorFlow保存或加载训练的模型
  14. ucos任务调度函数 OSSched()函数分析 ,任务切换函数
  15. java hsqldb数据库,HSQLDB数据库的使用
  16. 电池电量显示模块、美容仪、剃须刀、血氧仪、红外体温计、脱毛器、(耳)额温枪、电子秤等段码屏LCD液晶显示驱动IC-VK1024B 6*4段显示,VK1056 14*4段显示,少脚位1621,具省电模式
  17. QQ自动发消息源代码
  18. matlab 检验异方差,stata中面板数据异方差的处理_stata面板异方差检验
  19. 政简网:还剩一个月时间怎么科学有效复习公务员考试?
  20. 一个农村小伙的淘宝创业故事

热门文章

  1. 强引用置为null,会不会被回收及内存分配及年轻代年老代算法回收
  2. java 处理物料清单_JAVA Spring MVC 物料清单BOM 展开实例
  3. 【毕业设计_课程设计】位置信息管理网站设计(源码+论文)
  4. 计算机四级维修工查询,计算机安装调试维修员(四级)技能鉴定试题单总汇.doc
  5. C语言中,#include的用法:#include 和 #include区别
  6. day01.我们为什么要学习进制
  7. Linux TC 带宽管理队列规则
  8. Gson的JsonParser使用
  9. Linux 配置本地域名项目
  10. Linux基础:文件类型