写在开头
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
一个进程可以有很多线程,每条线程并行执行不同的任务。
今天我们说一下Java编程的很重要的一部分:线程.
线程
程序
- 程序是是一组指令的有序集合,是具有一定的逻辑和数据的处理能力的代码的集合
进程
- 进行是在内存中运行的程序,是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位
线程
- 线程是进进程的一个实体,是CPU调度和分派的基本单位.进程中的一个执行通道,叫做线程.进程中如果有多个执行通道,那么就称为多线程,线程通道之间彼此是独立的,互不干扰的
定义啥的每个人解释的都不尽相同,打开计算机的资源管理器,里面就显示着当前正在运行的程序
再逼逼叨几句,以便更好的理解三者
1 | 一个程序至少有一个进程,一个进程至少有一个线程. |
并行和并发
并行性和并发性是既相似又有区别的两个概念。
并行性是指两个或多个事件在同一时刻发生。
而并发性是指连个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机环境下(一个处理器),每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。例如,在1秒钟时间内,0-15ms程序A运行;15-30ms程序B运行;30-45ms程序C运行;45-60ms程序D运行,因此可以说,在1秒钟时间间隔内,宏观上有四道程序在同时运行,但微观上,程序A、B、C、D是分时地交替执行的。
多线程的实现方式
继承Thread类
- Thread: 类,就是表示一个线程,表示程序中一条执行通路,Java虚拟机允许应用程序同时执行多个线程
直接上代码,使用方法在代码中以注释的形式诠释
1 | package io.cnfox.thread; |
1 | package io.cnfox.thread; |
运行结果具有随机性,但是由于线程循环次数较少没有体现出来
1 | main---0 |
实现Runnable接口
- Runnable : 接口,用于实现一个线程
直接上代码,使用方法在代码中以注释的形式诠释
1 | package io.cnfox.thread; |
1 | package io.cnfox.thread; |
运行结果具有随机性,但是由于线程循环次数较少没有体现出来
1 |
|
Thread类和Runnable实现线程的区别
实现方式不同,Thread需要继承,Runnable需要实现
内部方法调用不同
Thread 类.调用start方法,JVM默认调用当前执行线程的run方法(重写),JVM默认调用这处,看不到代码
Runnable 接口: 将实现类作为参数传递进Thread的构造方法中,通过一些列的init方法,最终将实现类给Runnable类型的成员进行赋值, 调用run方法,判断,如果传递的实现类为null,什么都不错;如果传递的不是null,那么就调用这个实现累的run方法在线程中运行使用
Thread ,使用起来简单,但是类之间的耦合性增加了,原因: 一个类只能有一个直接父类
Runnable,使用比较复杂,但是类与类之间的耦合性屏蔽掉,接口是可以多实现的总结: 多数情况下,更加倾向于使用Runnable接口.
匿名Thread类&匿名Runnable接口
1 | package io.cnfox.thread; |
线程名称的相关内容
- getName() :获取当前线程的名字,返回值类型String类型
线程的命名规则:Thread-x x就表示数字,数字从0开始,一直往后,每次x+1
1 | package io.cnfox.thread; |
- setName(String name): 给线程设置名字
- Thread的构造方法,也可以写线程设置名字
举例: Thread(Runnable target, String name)
1 | package io.cnfox.thread; |
1 | package io.cnfox.thread; |
1 | package io.cnfox.thread; |
获取正在运行的线程对象
程序—方法—逻辑, 逻辑如果要执行的话,必须要写在方法内部,如果方法可以执行的话,程序中一定是有线程(通路)来执行这个方法,证明有线程正在运行,因此可以获取到正在运行的线程的对象
Thread 类中,有一个static cuttentThread() 方法,返回值类型是Thread类的一个对象
举例:
Thread.currentThread().getName()
1 | package io.cnfox.thread; |
1 | package io.cnfox.thread; |
运行结果具有随机性,但是由于线程循环次数较少没有体现出来
1 | 小倩0 |
线程的休眠
sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。
使用方法:
sleep(long millis) 线程睡眠 millis 毫秒
sleep(long millis, int nanos) 线程睡眠 millis 毫秒 + nanos 纳秒
如何调用:
因为sleep()是静态方法,所以最好的调用方法就是 Thread.sleep()。
调用位置:
线程的sleep方法应该写在线程的run()方法里,就能让对应的线程睡眠。
sleep 方法会抛出异常,java.lang.InterruptedException
正在进行等待或者休眠的线程,被打断了,线程终止了,抛出上述异常
run方法来自于父类或者父接口,父类针对于run没有抛出任何异常,子类重写run时,也不能抛出异常,可以在run方法内部处理异常(try…catch)
1 | package io.cnfox.thread; |
守护线程
守护线程就像是一个配角,如果非守护线程都没有结果,守护线程就可以正常运行,如果非守护线程都已经结束,守护线程就结束执行.
这样说可能有点抽象,举一个栗子:pc版本的qq,聊天的对话框和QQ主界面这两个线程同时,你关闭了聊天的对话框,QQ主界面不会被关闭,但是如果你关闭了QQ主界面,聊天的对话框就会被程序关闭.
在这个栗子里,聊天的对话框就是一个守护线程,而QQ主界面就是一个非守护线程~
1 | package io.cnfox.thread; |
线程的优先级
线程的优先级: 作用,优先级高的线程先执行,多执行,会多占用CPU的资源,但是多线程的运行,就算线程之间具有优先级,结果仍然具有随机性
方法setPriority(int newPriority) : 更改线程的优先级,参数int值越大,线程的优先级就越高
Thread类中的静态变量,标识线程的优先级,变量都是int类型
MAX_PRIORITY 最大线程优先级别 ———- 10
MIN_PRIORITY 最小的线程优先级别——— 1
NORM_PRIORITY 默认的线程优先级别——— 5
1 | package io.cnfox.thread; |
线程安全
线程安全: 多个线程同时在操作同一部分数据,有可能或发生线程安全问题
电影院卖票线程安全问题案例
要求: 电影院 <千与千寻> ,这一场卖100张票,座位号1-100
卖票要求: 线上线下同步销售, 从四个渠道 美团网,猫眼,影院,淘宝 都可以销售影票
同步代码块解决线程安全
同步代码块,格式:
synchronized(对象锁){
// 可能会发生线程安全的代码
}
synchronized : 同步,将线程的处理逻辑,放置到同步代码块中,就能解决线程的安全问题
(对象锁) : 就是一个对象,要求,如果多个线程同时执行同步代码块中的内容,对象锁必须是同一个对象
{}: 大括号中,写上可能会发生线程安全的代码
总结:
- 线程进同步代码块执行程序,先判断同步的对象锁是否存在
存在,将对象锁获取到,取走,进入同步代码块中执行
不存在,不能进入同步代码块进行代码的执行,只能在同步外等待 - 线程执行完同步代码块中的内容,出同步,将锁对象归还给同步代码块
- 有了锁对象,其他资源都可以竞争,进入同步执行,进同步又拿走同步锁,以此类推
- 一定要保证,多个线程使用对象锁是唯一的,如果同步锁不唯一,仍然无法保持线程的安全
以下代码已实现同步代码块解决线程安全
1 | package io.cnfox.thread; |
1 | package io.cnfox.thread; |
晚安
今天就到这里了,明天见,加油!