一、JUC概述
1.1 什么是JUC
java.util.concurrent下的并发工具类包。
1.2 进程和线程的概念
进程
是计算机中程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。
线程
是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并发执行不同的任务。
- 进程:指的是系统中正在运行的一个应用程序;程序一旦运行就是进程,进程--资源分配的最小单位
- 线程:系统分配处理器时间资源的基本单位,或者说进程之内独立执行的一个单元执行流。线程--程序执行的最小单位。
1.3 线程的状态
1.3.1 状态
- NEW
- RUNNABLE
- BLOCKED
- WAITING(等待)
- TIME_WAITING(过时不候)
- TERMINATED(终止)
1.3.2 wait 和 sleep区别
- sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用。
- sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占用锁(即代码要在synchronized中)。
- 他们都可以被interrupted方法中断。
1.4 并发和并行
串行模式
串行表示所有任务都---按先后顺序进行。串行意味着必须先装完一车柴才能运送这车柴,只有运送到了,才能卸下这车柴。只有完成这三个步骤才能进行下一步。
串行是一次只能取得一个任务,并执行这个任务。
并行模式
并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。
多项工作一起执行,之后在汇总
并发模式
并发指的是多个程序可以同时运行的现象,更细化的是多进程可以同时运行或者多指令可以同时运行。
同一时刻多个线程在访问同一资源,多个线程对一个点
1.5 用户线程和守护线程
用户线程:自定义线程
守护线程:后台线程,比如垃圾回收线程。
若设置为守护线程则主线程结束,就结束
二、Lock接口
2.1 复习Synchronized
synchronized是Java 中的关键字,是一种同步锁。他修饰的对象有以下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}
- 修饰一个方法,被修饰的方法称为同步方法,其作用为整个方法。(
静态方法、实例方法锁的对象不同
)
2.2 Lock和Synchronized不同
- Lock是一个接口,而synchronized 是JAVA中的关键字,synchronized是内置的语言实现。
- synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unlock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。
- Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。
- 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
- Lock可以提高多个线程进行读操作的效率。
2.3 多线程编程步骤
- 第一步 创建资源类,在资源类创建属性和操作方法
- 第二步 在资源类操作方法
- 判断
- 干活
- 通知
- 第三步 创建多个线程,调用资源类的操作方法
// 1. 创建资源类,定义属性和方法
class Share{
//初始值
private int number = 0;
// +1
public synchronized void incr() throws InterruptedException {
//判断 干活 通知
if (number!=0){
//判断number值是否为0.如果不是0,等待
this.wait();
}
//如果number是0,就+1操作
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知线程
this.notifyAll();
}
// -1
public synchronized void decr() throws InterruptedException {
//判断 干活 通知
if (number==0){
//判断number值是否为0.如果不是0,等待
this.wait();
}
//如果number是0,就+1操作
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知线程
this.notifyAll();
}
}
public class ThreadDemo01 {
// 第三步 创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
Share share = new Share();
//创建线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr();
}catch (InterruptedException e){
e.printStackTrace();
}
}
},"AA").start();
//创建线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr();
}catch (InterruptedException e){
e.printStackTrace();
}
}
},"BB").start();
}
}
使用Lock实现
// 第一步 创建资源类 ,定义属性和操作方法
class share{
private int number = 0;
// 创建 lock
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//+1
public void incr(){
// 上锁
lock.lock();
try {
//判断
while (number!=0){
condition.await();
}
//干活
//如果number是0,就+1操作
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
// 通知
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
//-1
public void decr(){
// 上锁
lock.lock();
try {
//判断
while (number==0){
condition.await();
}
//干活
//如果number是0,就+1操作
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
// 通知
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
正确的输出结果
当我们在加两个线程,会出现虚假唤醒的现象
线程安全出现问题,是因为出现
虚假唤醒
的问题。
为什么会出现虚假唤醒现象
例如飞机的安检,只要下了飞机再上飞机都要做安检
。
改为while即为正常