Java 多线程
多线程理论基础
提示
本文用于理解学习多线程的理论基础以及代码实现
带着BAT大厂的面试问题去理解
- 描述进程和线程的区别
- 如何理解并发和并行的区别
- 多线程的出现是要解决什么问题?
- 线程不安全是指什么?举例说明
- 实现线程安全有哪些方法?
进程的发明
最初的计算机只能执行一些特定的指令,用户输入一个指令,计算机做出一个操作,显然计算机大部分时间都在等待用户的指令,效率低下。
批处理操作系统:用户将一系列指令写下来,形成一个清单,然后交给计算机去读取并依次执行指令,这样批处理系统就出现了
多任务操作系统:虽然批处理系统的诞生提高了电脑的处理效率,但是一个操作执行完,才能执行另一个操作,当一个操作需要大量的IO操作,cpu 只能等待IO操作完成才能执行其他操作,很浪费 cpu 的资源。于是人们发明了进程,每个进程对应一定的内存空间,且只能使用自己内存,每个进程之间互不干扰。同时进程也保存了程序的运行状态,当程序暂停时,在下一次切换会根据暂停时保存的状态来恢复,并接着执行。
可以说 进程的出现提高了 cpu 的利用率
并发和并行的概念
并发是能够让操作系统从宏观上看起来同一时间段执行多个任务。 换句话说,进程让操作体统的并发成
为了可能,至此出现多任务操作系统。
虽然并发从宏观上看有多个任务在执行,但在事实上,对于单核CPU来说,任意具体时刻都只有一个任
务在占用CPU资源。
操作系统一般通过CPU时间片轮转实现并发
- 并发:在一段时间内多个进程轮流使用 cpu,多个进程形成并发
- 并行:在同一时刻多个进程使用各自的 cpu ,多个进程形成并行,并行需要多个 cpu 支持
线程的出现
出现进程后,计算机性能 cpu 得到了巨大的提升,但是人们对实时性有了要求,因为一个进程在一个时间段内只能做一个事情,如果一个进程有多个子任务时,只能逐个得执行这些子任务,很影响效率。
e.g:当手机在获取网络图片时,此时用户点击了相机,因为 cpu 正在渲染图片,不能响应点击相机的操作,就出现了卡顿现象
所以人们为了解决实时性,发明了 线程,让一个线程执行一个子任务,这样一个进程就包含了多个线程;当用户点击相机时,暂停渲染图片,响应相机操作,响应完成再切换,就满足了对实时性的要求。
进程让操作系统的并发性成为了可能,而线程让进程的内部并发成为了可能
提示
注意:一个进程包含多个线程,但是这些线程共享进程占有的内存地址空间和资源。进程是操作系统进
行资源分配的基本单位(进程之间互不干扰),而线程是操作系统进行CPU调度的基本单位(线程间互
相切换)
进程和线程的区别
根本区别:进程是操作系统资源分配的基本单位,而线程是CPU调度和执行的基本单位
- 开销:进程有独立的内存空间,程序之间切换开销比较大;线程可以看出是轻量级的进程,共享进程的资源,每个线程都有自己独立的栈空间,切换开销小
- 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系
统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源 - 包含关系:线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务
线程不安全实例
模拟买票过程,共有 5 张票,多线程模拟卖票的过程,以下代码模拟买票过程,操作结束后输出的数据错乱,有可能出现 票数为负数的结果
public class TicketRunnable implements Runnable {
private int count = 5;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (count > 0) {
count--;
System.out.println(Thread.currentThread().getName() + "=> " + count);
}
}
}
}
模拟买票
public class TicketRunnableExample {
public static void main(String[] args) {
TicketRunnable tr = new TicketRunnable();
Thread th1 = new Thread(tr);
Thread th2 = new Thread(tr);
Thread th3 = new Thread(tr);
th1.start();
th2.start();
th3.start();
// 输出:
// Thread-0=> 4
// Thread-0=> 1
// Thread-0=> 0
// Thread-2=> 2
// Thread-1=> 3
}
}
实现线程安全
1. 互斥同步
- synchronized: Java的关键字,同步机制由 Jvm 维护
- ReentrantLock: Java 并发包中的互斥锁
如需要进一步了解线程互斥同步可以看: