当前位置:网站首页 > Java基础 > 正文

java基础 多线程



目录

一、线程的基本使用

(一)创建线程的两种方式

(二)线程简单案例(Thread)

问题:main函数与开启的线程是否是阻塞的,即线程运行时,main函数等待线程运行结束?

问题:为什么不直接调用cat的run方法,而是通过start来使用?

start解析

 (三)线程简单案例(Runnable)

(四)线程简单案例(多线程)

二、线程终止

java基础 多线程

三、线程的常用方法

(一)常用方法一

(二)常用方法二

(三)用户线程和守护线程

四、线程生命周期

五、线程的同步

六、互斥锁

七、线程死锁

八、释放锁

(一)下面操作会释放锁

(二)下面操作不会释放锁


一、线程的基本使用

(一)创建线程的两种方式

在Java中线程使用有两种方式:

① 继承Thread类,重写run方法

② 实现Runnable接口,重写run方法

(二)线程简单案例(Thread)

由主函数开启一个线程,每秒钟输出一句话

 

问题:main函数与开启的线程是否是阻塞的,即线程运行时,main函数等待线程运行结束?

验证

(1)方式一

首先改进main函数,运行

 

查看两个线程的名字

 

说明主线程与开启的线程是并行的,即多线程

 (2)方式二

使用Jconsole(jdk自带的线程监控工具)查看线程,首先扩大输出语句的次数方便我们验证

 

运行main函数后,在Terminal输入Jconsole

 main线程和Thread-0线程都在运行

main线程运行结束后,Thread-0线程还在继续运行 

 Thread-0线程运行结束,此时连接中断

总结:当main函数运行结束时,不意味着main函数开启的线程也结束。

问题:为什么不直接调用cat的run方法,而是通过start来使用?

解释:如果调用cat,run()来执行,此时run方法里输出的线程名字将会是main,意味着并没有开启线程,而且此时是串行化的执行,需要run方法运行结束才会继续执行main下面的内容

验证

 

start解析

查看start方法中,我们可以发现调用了start0方法,该方法是一个native(本地)方法,由JVM调用,底层是c/c++实现,真正实现多线程的效果, 是 start0(), 而不是 run

 

 (三)线程简单案例(Runnable)

java是单继承的,在某些情况下一个类可能已经继承了父类,此时无法通过继承Thread类创建线程,因此可以通过实现Runnable接口来创建线程 ,这里使用了代理模式,通过Thread类进行代理,最终调用的是实现Runnable接口的dog的run方法。       

 

(四)线程简单案例(多线程)

 

总结继承Thread   VS    接口Runnable

从java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,都是start()-->start0(), 从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口

实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable

使用继承方式的好处是,在 run() 方法内获取当前线程直接使用 this 就可以了,无须使用Thread.currentThread0方法;不好的地方是 Java 不支持多继承,如果继承了Thread类那么就不能再继承其他类。另外任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码,而 Runable 则没有这个限制。 

二、线程终止

当线程完成任务后会自动退出,也可以通过使用变量控制run方法退出的方式停止线程,即通知方式

 

三、线程的常用方法

(一)常用方法一

  1. setName  //设置线程名称,使之与参数 name 相同
  2. getName  //返回该线程的名称
  3. start  //使该线程开始执行; Java 虚拟机底层调用该线程的 start0 方法
  4. run  //调用线程对象 run 方法,直接调用不会创建新的线程
  5. setPriority  //更改线程的优先级
  6. getPriority  //获取线程的优先级
  7. sleep //线程的静态方法,在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
  8. interrupt  //中断线程,但没有真正终止线程,一般用于中断正在休眠的线程

线程优先级

案例:

 

(二)常用方法二

  1. yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
  2. join: 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

join解释图

案例: main线程创建一个子线程,每隔1s 输出 hello输出 20次,主线程每隔1秒,输出 hi,输出 20次.要求两个线程同时执行,当主线程输出 5次后,就让子线程运行完毕,主线程再继续

 

 yield

 

(三)用户线程和守护线程

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

案例:子线程循环次数大于main线程,当main线程结束后,子线程也需要结束

 

四、线程生命周期

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

有的书籍会说7种状态,是将Runnable状态分为了Ready(就绪态)和Running(运行态) 

 查看线程状态

 

五、线程的同步

可以 通过加锁实现线程的同步,比如Synchronized关键字

1、同步代码块

 

2、同步方法

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

 

六、互斥锁

  1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
  2. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
  3. 关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时, 表明该对象在任一时刻只能由一个线程访问
  4. 同步的局限性:导致程序的执行效率要降低
  5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
  6. 同步方法(静态的)的锁为当前类本身

注意事项和细节:

  1. 同步方法如果没有使用static修饰:默认锁对象为this
  2. 如果方法使用static修饰,默认锁对象:当前类.class
  3. 实现的落地步骤:
  • 需要先分析上锁的代码
  • 选择同步代码块或同步方法
  • 要求多个线程的锁对象为同一个即可! 

七、线程死锁

案例:

妈妈:你先完成作业,才让你玩手机

小明:你先让我玩手机,我才完成作业

 

八、释放锁

(一)下面操作会释放锁

(二)下面操作不会释放锁

  • 上一篇: java基础107
  • 下一篇: java基础教学入门
  • 版权声明


    相关文章:

  • java基础1072025-04-19 20:50:06
  • java基础教程ppt下载2025-04-19 20:50:06
  • java基础尖锐问题2025-04-19 20:50:06
  • java基础循环串讲2025-04-19 20:50:06
  • java内存基础2025-04-19 20:50:06
  • java基础教学入门2025-04-19 20:50:06
  • java零基础指南2025-04-19 20:50:06
  • 基础java培训机构2025-04-19 20:50:06
  • Java script基础2025-04-19 20:50:06
  • 基础java考试2025-04-19 20:50:06