JavaSE进阶(一)——多线程概述
前言
从本篇笔记开始我们进入到JavaSE进阶阶段的笔记记录,具体记录顺序是:多线程、集合、网络编程、注解和反射、GUI(简单记录)。当这些记录完毕后会开始JavaWeb方面的记录。
多线程概述
多线程就是一心多用,我们在日常的时候可以同时处理很多事。一边吃饭、一边刷剧;一边做家务,一边唱歌。其实在计算机中也是存在这种一心多用的情况。比如:我们一边打游戏一边听音乐;一边语音通话一边打字。
先说明一个概念,在用户角度来看好像计算机在同时处理多个进程,实际上是在不停地在多个程序间切换执行,只是切换速度特别快,用户无法察觉。现在我们的电脑都是多核CPU的,也就是说就算真的同时执行不同程序,也是不同CPU之间的合作。记住这个现象,一个CPU在一个单位时间内只能处理一个工作。
我们研究多线程的时候是建立在一个CPU的基础下,多个CPU对于我们研究多线程的本质以及学习多线程的使用并没有什么帮助,底层原理都一样。
进程
程序是指令和数据的有序集合,本身属于静态,有了执行的操作才会是动态概念,这时候就被称为进程。程序运行需要系统分配资源,所以进程是系统分配资源的单位;
线程
一个进程可以包含若干个线程,一个进程中至少包含一个线程(主线程)。线程是我们本篇笔记研究的核心,线程就是CPU调度和执行的单位。
线程中包含很多概念以及特征,具体如下:
-
线程是独立的执行路径;
-
程序运行时,即使自己没有创建线程,后台也会有多个线程:主线程、GC线程(垃圾回收线程)。
【注】:垃圾回收会在Java虚拟机中解释说明,偏向底层的知识点,本阶段不涉及。
-
main()称为主线程,是程序的入口,用来执行整个程序;
-
如果程序的运行开辟了多线程,则线程的运行顺序是由调度器安排的,调度器与操作系统相关,无法人为干预;
-
对同一份资源进行操作时,会存在自愿抢夺问题,需要加入并发控制;
-
每个现场在自己的工作内存中交互,内存控制不当会造成数据不一致。
以上问题在操作系统这门课中有详细的解释,随便找一个视频都可以学明白,所以在此不做赘述,接下来介绍在Java中如何使用多线程。
线程使用
线程创建
线程一共有三种创建方式:
- 继承Thread类;
- 实现Runnable接口;
- 实现Callable接口;
Thread类
继承Thread类,并重写run方法:
// 1.创建类并继承Thread类
public class TestThread extends Thread {
// 2.重写run方法
@Override
public void run() {
int i = 0;
while (true){
System.out.println("我在敲代码");
}
}
public static void main(String[] args) {
// 3.在主线程创建自定义线程类的对象
TestThread testThread = new TestThread();
int i = 0;
// 4.调用start方法
testThread.start();
// testThread.run();
while (true){
System.out.println("我在玩游戏");
}
}
}
以上代码中存在一个问题,看看有没有能找到。
代码在执行过程中,会出现主方法和调用方法中输出的语句交替运行的状况。具体原因如下:
- 调用run方法:这个调用方式和调用普通方法的流程一样,必须在调用方法执行结束之后才能继续执行主线程;可以理解为调用run方法的时候并没有开启线程;
- 执行start方法:这个方式是开辟一条线程出来执行run方法,主线程和子线程交替进行,没有先后顺序,顺序由调度器来定义。
Runnable接口
实现Runnable接口,实现run方法
// 1.创建自定义线程类并实现Runnable接口
public class MyRunnable implements Runnable{
// 2.实现run方法
@Override
public void run(){
int i = 0;
while(i < 10){
System.out.println("子线程");
i ++;
}
}
public static void main(String[] args){
// 3.创建自定义类对象
MyRunnable myRunnable = new MyRunnable();
// 4.我们需要用装饰模式来完成自己想要执行的自定义子线程
Thread thread = new Thread(myRunnable);
// 5.使用装饰过的对象来调用start方法
thread.start();
while(i < 10){
System.out.println("主线程");
i ++;
}
}
}
线程等待
我们在使用多线程操作的时候我们经常会出现某个线程需要一会在执行的操作,这个时候需要sleep方法来执行
@Override
public void run(){
// 线程会等待1000毫秒后再执行
Thread.sleep(1000);
}
实现Callable接口(了解即可)
该接口仅做了解即可,进入公司工作后也不一定会使用该接口(阿里不推荐使用此接口),在此仅做简单介绍。
执行步骤:
- 实现Callable接口,需要返回值类型;
- 重写call方法,需要抛出异常;
- 穿件目标对象;
- 创建执行服务:
ExcutorService s = Excutors.newFixedThreadPool(1);
- 提交执行:
Future<Boolean> result1 = s.submit(t1);
- 获取结果:
boolean r1 = result1.get();
- 关闭服务:
s.shutdownNow();
总结
多线程的基础知识在这里,其实多线程更多的是基于业务来操作,本身并没有特别多的知识点,各位可以结合相关业务来尝试开发一下。