目录
一.线程创建
线程基础知识介绍
创建方法一:继承Thread类
创建方法二:实现Runnable接口
创建方法三:实现Callable接口;线程池
线程池
Callable接口实现多线程
二.线程状态
线程状态
java线程基础课程
线程状态转换
代码演示
三.线程优先级
四.用户线程,守护线程
五.线程同步
同步方法
同步代码块
可重入锁ReentrantLock
详细讲解,强烈建议收藏!!!
一.线程创建
线程基础知识介绍
什么是线程?为什么要使用线程?
在没有使用到线程的程序中,我们总是只能串行的执行程序,也就是我们的程序一次性只能进行一个操作。而我们使用微信进行视频聊天的时候,我们可以一边视频,一边和其他人打字聊天。这就是多线程,可以一个线程负责视频聊天,一个线程负责打字聊天,使用的任然是一个程序。在日常使用场景中,多线程是必要的。
创建方法一:继承Thread类
Thread单词意思就是线程的意思,是Java提供的一个线程类。可以自己创建一个类然后继承Thread,重写run方法。run方法写入你想要进行多线程运行的代码。
通过你建的类,实例化一个对象;通过这个对象调用start()方法,该对象就可以进行多线程操作了。
创建步骤:
1.继承Thread类,重写run方法
2.实例化一个对象,调用start方法(就是执行run函数里面的内容)
代码演示:
运行结果发现,Thread1线程和Thread2线程轮流上处理机执行,由此可知该段程序确实实现了多线程操作
创建方法二:实现Runnable接口
Runnable是一个接口,该接口是一个函数式接口(不知道函数式接口的伙伴,可以看看我上一篇博客哦),只有一个抽象方法run
创建方法一的Thread类就是实现了Runnable接口,查看源码如下图:
那为什么已经有Java开发人员已经给我们封装好的Thread类,有更多的方法可以供我们调用,我们还要用Runnable接口来实现线程,不是很冗余嘛,容我卖个关子,会在代码中解释。
创建步骤:
1 创建一个类实现Runnable接口,重写run方法
2 通过该类实例化对象
3 将实例化的对象作为参数传到Thread实现线程,再调用start方法
上面代码用到的构造器:
细心的伙伴在运行代码的时候会发现,票数可能出现负数的情况,这是明显的异常,原因是多个线程买了同一张票,为了解决这个问题,需要用到线程同步的知识,将在后续章节介绍。
创建方法三:实现Callable接口;线程池
Callable也是一个接口,可以使用到线程池。当多线程场景下,我们一直创建或者销毁线程,对性能影响都很大,如果我们提前创建好多个线程,放入线程池,使用时直接获取,使用完放回池中,就可以避免一直创建、销毁,达到重复利用。
线程池
ExecutorService 线程池接口
常用方法:
void execute(Runnable command); 执行任务,没有返回值,一般用来执行Runnable
<T> Future<T> submit(Callable<T> task);执行任务,有返回值,一般用来执行Callable
void shutdown();关闭连接池
Executors 该类用于创建线程池
Callable接口实现多线程
实现callable接口需要返回值类型 ,而上述两种方法是不需要返回值的。不同与上述两种方法,实现Callable需要重写call方法,而不是run方法,并且call方法需要抛出异常。
创建步骤:
1 创建一个类实现Callable接口,重写call方法
2 创建目标对象,即通过该类实例化对象
3 创建执行的服务,可通过实现线程池来创建
4 提交执行 callable为submit方法
5 获取结果
6 关闭服务
二.线程状态
线程状态
创建:当线程被创建时为创建状态
就绪:线程被分配了除处理机以外的所有资源
运行:线程正在处理机上运行
阻塞:线程还缺乏除处理机以外的其他资源导致无法继续执行
终止:线程执行结束
线程状态转换
创建到就绪:线程被分配足够资源就到了就绪状态,可以随时上处理机,理论上创建好的线程调用start方法即可进入就绪状态
就绪到运行:该转换可能又操作系统自行安排,也可通过程序定义的抢占来完成,join方法就是抢占方法,只有join的线程执行完,其他的才能执行
运行到就绪大有可能就是有线程抢占了处理机资源,也可以使用yield方法线程自己礼让
运行到阻塞:除了线程缺乏资源只能阻塞,也可以调用sleep方法,线程自行休眠
代码演示
下面对上述及其他线程状态有关方法演示:
三.线程优先级
上文介绍线程状态切换中有个抢占算法join,什么时候需要抢占呢?其中有个使用场景就是,线程优先级是不同的,高优先级可以抢占低优先级的处理机资源。
Java线程按照数值划分等级,从1~10数值越大等级越高。
可以通过getPriority()这个方法获取线程的优先级
通过setPriority()设置线程的优先级,数值可以从0~10
还提供了三种常量可供选择。
未设置优先级,默认为5.
下面进行代码演示
四.用户线程,守护线程
五.线程同步
多个线程操作同一个对象,就可能造成同步问题,如上文买票,可能多个线程对同一张票进行操作。解决方案可以让多个线程排队 ,,锁唯对操作对象的线程上锁一,就能避免同步问题
同步方法
使用synchronized修饰一个方法,就实现了同步方法,整个方法体都会进行同步;同时只会有一个线程执行这个方法。实际就是对这个方法加上了锁,锁唯一,有一个线程执行了这个方法,就上锁了,其他线程不能进入,就解决了同步问题。
下面优化代码,解决上文遗留的买票问题
同步代码块
使用synchronized定义一段代码块,对一段代码块或者代码块里的共享变量进行上锁。
上文buy方法也可以用同步代码块实现。
在取钱场景中,一个账户,同时在手机端和ATM机上取钱 ,也存在同步问题。账户就作为了共享变量,多个线程都可以进行操作。此时将取钱方法使用synchronized修饰,变为同步方法,由于对共享变量更新不及时,依然可能存在同步问题。
此时使用同步代码块对共享变量进行上锁可以完全解决同步问题。
执行结果
依然存在问题,使用同步代码块:
得到结果
同步问题解决。
在实际开发中,可以尽量使用同步代码块,一个方法体并非所有部分都需要上锁,使用同步代码块更加灵活。并且同步代码块我们可以自由选择锁,对于共享变量的处理,使用同步代码块能更好解决问题。
可重入锁ReentrantLock
同步代码块、同步方法都是系统帮助我们上锁,解锁。可重入锁可以显示的上锁,解锁。
如有帮助,烦请点赞关注!
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.bianchenghao6.com/h6javajc/20152.html