目录
一、线程的基本使用
(一)创建线程的两种方式
(二)线程简单案例(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方法退出的方式停止线程,即通知方式
三、线程的常用方法
(一)常用方法一
- setName //设置线程名称,使之与参数 name 相同
- getName //返回该线程的名称
- start //使该线程开始执行; Java 虚拟机底层调用该线程的 start0 方法
- run //调用线程对象 run 方法,直接调用不会创建新的线程
- setPriority //更改线程的优先级
- getPriority //获取线程的优先级
- sleep //线程的静态方法,在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
- interrupt //中断线程,但没有真正终止线程,一般用于中断正在休眠的线程
线程优先级
案例:
(二)常用方法二
- yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
- join: 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
join解释图
案例: main线程创建一个子线程,每隔1s 输出 hello输出 20次,主线程每隔1秒,输出 hi,输出 20次.要求两个线程同时执行,当主线程输出 5次后,就让子线程运行完毕,主线程再继续
yield
(三)用户线程和守护线程
- 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
- 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
- 常见的守护线程:垃圾回收机制
案例:子线程循环次数大于main线程,当main线程结束后,子线程也需要结束
四、线程生命周期

有的书籍会说7种状态,是将Runnable状态分为了Ready(就绪态)和Running(运行态)
查看线程状态
五、线程的同步
可以 通过加锁实现线程的同步,比如Synchronized关键字
1、同步代码块
2、同步方法
Synchronized还可以放在方法声明中,表示整个方法为同步方法
六、互斥锁
- Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
- 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
- 关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时, 表明该对象在任一时刻只能由一个线程访问
- 同步的局限性:导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
- 同步方法(静态的)的锁为当前类本身
注意事项和细节:
- 同步方法如果没有使用static修饰:默认锁对象为this
- 如果方法使用static修饰,默认锁对象:当前类.class
- 实现的落地步骤:
- 需要先分析上锁的代码
- 选择同步代码块或同步方法
- 要求多个线程的锁对象为同一个即可!
七、线程死锁
案例:
妈妈:你先完成作业,才让你玩手机
小明:你先让我玩手机,我才完成作业
八、释放锁
(一)下面操作会释放锁
(二)下面操作不会释放锁
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/1830.html