Java高级知识复盘之线程通信,线程死锁,线程特性,线程状态
线程死锁
线程通信
线程特性
线程状态
1 线程死锁
1.1 基础介绍
(1)线程之间出现彼此等待现象
等待对方释放对象锁
线程死锁 程序卡死
(2)当程序员使用synchronized和ReentrantLock方式不当时,就会出现死锁现象
(3)该现象需避免
1.2 synchronized
/**
* 同步锁synchronized:
* 线程死锁解决办法:锁顺序一致 | 不要嵌套使用
*/
private static void method2() {
Object o1 = new Object();
Object o2 = new Object();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + "获取到o1");
}
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + "获取到o2");
}
}
}, "threadA");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + "获取到o2");
}
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + "获取到o1");
}
}
}, "threadB");
threadA.start();
threadB.start();
}
1.3 ReentrantLock
1.3.1
/**
* 显示锁ReentrantLock:
* 线程死锁解决办法:锁顺序一致 | 不要互相嵌套
*/
private static void method4() {
ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
lock1.lock();
try {
System.out.println(Thread.currentThread().getName() + " lock1");
} finally {
lock1.unlock();
}
lock2.lock();
try {
System.out.println(Thread.currentThread().getName() + " lock2");
} finally {
lock2.unlock();
}
}
}, "threadA");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
lock1.lock();
try {
System.out.println(Thread.currentThread().getName() + " lock1");
} finally {
lock1.unlock();
}
lock2.lock();
try {
System.out.println(Thread.currentThread().getName() + " lock2");
} finally {
lock2.unlock();
}
}
}, "threadB");
threadA.start();
threadB.start();
}
1.3.2
/**
* 显示锁ReentrantLock:
* 线程死锁解决办法:
* tryLock():
* 尝试获取锁
* 若能获取锁,返回值为true;反之为false
*
* tryLock(num,TimeUnit):
* 尝试获取锁,若不能获取锁,最多等待多久
* 若能获取锁,返回值为true;反之为false
*/
private static void method5() {
ReentrantLock lock1 = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
lock1.lock();
try {
System.out.println(Thread.currentThread().getName() + " lock1");
/**
* tryLock():
* 尝试获取锁
* 若能获取锁,返回值为true;反之为false
*
* tryLock(num,TimeUnit):
* 尝试获取锁,若不能获取锁,最多等待多久
* 若能获取锁,返回值为true;反之为false
*/
if (lock2.tryLock(3, TimeUnit.SECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + " lock2");
} finally {
lock2.unlock();
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock1.unlock();
}
}
}, "threadA");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
lock2.lock();
try {
System.out.println(Thread.currentThread().getName() + " lock2");
if (lock1.tryLock(3, TimeUnit.SECONDS)) {
try {
System.out.println(Thread.currentThread().getName() + " lock1");
} finally {
lock1.unlock();
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock2.unlock();
}
}
}, "threadB");
threadA.start();
threadB.start();
}
2 线程通信(重要)
2.1 基础介绍
(1)线程交互
(2)内容:
共享主存;
生产者消费者模式 相对而言
2.2 案例
2.2.1 synchronized
package com.javasm.thread.exchange;
/**
* @author: ShangMa
* @className: WareHouse
* @description: 存放任务的仓库
* @date: 2022/8/17 10:48
*/
public class WareHouse {
// 仓库已有任务量
private Integer num = 0;
// 仓库最大任务容纳量
private final Integer MAX = 100;
/**
* 模拟:向仓库中存任务
*
* 生产任务
*/
public void put() {
synchronized (this) {
while (num >= MAX) {
// 仓库已满
System.out.println("仓库已满,生产者线程停止");
/**
* 生产者线程停止:
* sleep():不主动释放对象锁,在此不合适
* wait():
* 释放对象锁
* 进入线程的对象的等待池中 等待
*
* sleep()和wait()区别?
*
* sleep方法是Thread的静态方法,wait方法是Object的实例方法;
* sleep方法可以作用在任何位置,wait方法可以作用于同步方法或同步代码块中;
* sleep方法不释放对象锁,wait方法释放对象锁
*/
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
num++;
System.out.println(Thread.currentThread().getName() + "生产了一个任务:" + num);
/**
* notify():
* 随机唤醒等待池中的某个消费者线程
* notifyAll():
* 唤醒生产者等待池中的所有消费者线程
*
*/
this.notifyAll();
}
}
/**
* 模拟:从仓库中取任务
* <p>
* 消费任务
*/
public void take() {
synchronized (this) {
while (num <= 0) {
System.out.println("仓库已空,消费者线程停止");
try {
// 让消费者线程停止
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
num--;
System.out.println(Thread.currentThread().getName() + "消费了一个任务:" + num);
/**
* notify():
* 随机唤醒等待池中的某个生产者线程
* notifyAll():
* 唤醒生产者等待池中的所有生产者线程
*
*/
// this.notify();
this.notifyAll();
}
}
}
2.2.2 ReentrantLock
package com.javasm.thread.exchange;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author: ShangMa
* @className: WareHouse2
* @description: 存放任务的仓库类
* @date: 2022/8/17 14:51
*/
public class WareHouse2 {
// 仓库中当前的任务量
private Integer num = 0;
// 仓库中可容纳的最大任务量
private final Integer MAX = 100;
// 显示锁
private ReentrantLock lock1 = new ReentrantLock();
private Condition condition = lock1.newCondition();
/**
* 生产任务
* 生产者线程
*/
public void put() {
lock1.lock();
try {
while (num == MAX) {
System.out.println("仓库已满,生产者线程停止");
try {
// 让当前线程进入对象的等待池等待
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
num++;
System.out.println(Thread.currentThread().getName() + "生产了一个任务: " + num);
// 唤醒消费者线程执行任务
condition.signal();
} finally {
lock1.unlock();
}
}
/**
* 消费任务
* 消费者线程
*/
public void take() {
lock1.lock();
try {
while (num == 0) {
System.out.println("仓库已空,消费者线程停止");
try {
// 让消费者线程停止
condition.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
num--;
System.out.println(Thread.currentThread().getName() + "消费了一个任务:" + num);
/**
* signal():随机唤醒等待池中的任意一个生产者线程
* signalAll():唤醒待池中的所有生产者线程
*/
condition.signal();
} finally {
lock1.unlock();
}
}
}
2.3 sleep方法和wait方法区别(重要)
(1)sleep方法是Thread的静态方法,wait方法是Object的实例方法;
(2)sleep方法可以作用在任何位置,wait方法可以作用于同步方法或同步代码块中;
(3)sleep方法不释放对象锁,wait方法释放对象锁
3 线程特性(重要)
3.1 基础介绍
(1)原子性:
不可再分割
一系列操作要么全部都执行,要么全部都不执行
线程执行期间不被打断
synchronized 锁粒度较大
ReentrantLock 锁粒度较大
原子性类 锁粒度较小
(2)可见性:
一个线程对共享主存中的值操作,被其他线程看到
synchronized
ReentrantLock
volatile:
修饰成员变量;
保证成员变量在多线程之间的可见性;
强制要求:
线程每次都要从主存中获取最新值;
当对成员变量值修改后立马将修改后的值刷新回主存
(3)有序性:
代码的编写顺序和指令的执行顺序不一定一致
JVM会对指令重排序->提高性能
底层:
单线程环境下,程序结果不会受影响
多线程环境下,程序结果不保证
volatile:禁止JVM进行指令重排序
3.2 案例
3.2.1 可见性
package com.javasm.thread.features;
import java.util.concurrent.TimeUnit;
/**
* @author: ShangMa
* @className: FeaturesExercise
* @description: 线程特性
* @date: 2022/8/17 15:13
*/
public class FeaturesExercise {
/**
* volatile:
* 线程之间的可见性,不能保证线程之间的原子性
* 修饰成员变量->保证成员变量在多线程之间的可见性
*
* 强制要求线程每次都要从主存中获取最新值,当对成员变量值修改后立马将修改后的值刷新回主存
*/
private static volatile boolean isTrue = true;
private static volatile int num = 0;
public static void main(String[] args) {
method2();
}
/**
* volatile:
* 不能保证线程之间的原子性
*/
private static void method2() {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
num++;
}
}
}, "threadA");
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
num--;
}
}
}, "threadB");
threadA.start();
threadB.start();
try {
threadA.join();
threadB.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 结果不正确
System.out.println("结果:" + num);
}
/**
* volatile:
* 保证线程之间的可见性
*/
private static void method1() {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
while (isTrue) {//死循环
}
}
}, "threadA");
threadA.start();
try {
// 让主线程休眠5秒
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
isTrue = false;
System.out.println("程序结束");
}
}
8.2.2 有序性
3.2.2.1 饿汉模式
package com.javasm.thread.features;
/**
* @author: ShangMa
* @className: HungerSingleton
* @description: 饿汉单例模式
* @date: 2022/8/17 15:40
*/
public class HungerSingleton {
/**
* 饿汉单例模式:
* 类加载时,创建对象
* 类加载一次,创建一个对象
*/
// 私有的静态的属性
private static HungerSingleton singleton = new HungerSingleton();
// 私有的构造
private HungerSingleton() {
}
// 公开的静态的方法
public static HungerSingleton getSingleton() {
return singleton;
}
}
3.2.2.2 懒汉模式
package com.javasm.thread.features;
/**
* @author: ShangMa
* @className: LazySingleton
* @description: 懒汉单例模式
* @date: 2022/8/17 15:33
*/
public class LazySingleton {
// volatile:禁止指令重排序
private volatile static LazySingleton singleton;
private LazySingleton() {
}
public static LazySingleton getSingleton() {
/**
* 分配空间 2
* 创建对象 5
* 引用赋值 1
*
*/
// 双重检测 double check
if (singleton == null) {
synchronized (LazySingleton.class) {
if (singleton == null) {
singleton = new LazySingleton();
}
}
}
return singleton;
}
}
4 线程状态(重要)
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,// 新建,创建对象,但是还没被启动
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,// 可运行,启动还没被执行 等待os调度器调度
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,// 阻塞,等待对象锁(等其他线程释放对象锁) | 进入对象的等待池
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called {@code Object.wait()}
* on an object is waiting for another thread to call
* {@code Object.notify()} or {@code Object.notifyAll()} on
* that object. A thread that has called {@code Thread.join()}
* is waiting for a specified thread to terminate.
*/
WAITING,// 等待
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,// 限时的等待
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;// 终止 正常终止 | 不正常终止(异常)
}
5 线程池
5.1 基础介绍
(1)池化技术 复用资源,提高性能
(2)频繁创建线程、销毁线程,影响性能
(3)复用资源:
提前在线程池准备好一些线程;
任务提交给线程池,从线程池中挑选一个线程执行任务;
执行任务结束,线程会再次回到线程池中,方便接下一个任务;
当线程池关闭时,结束线程
5.2 案例
package com.javasm.thread.create;
import java.util.concurrent.*;
/**
* @author: ShangMa
* @className: ThreadPoolExercise
* @description: 线程池
* @date: 2022/8/17 16:12
*/
public class ThreadPoolExercise {
public static void main(String[] args) {
/*public ThreadPoolExecutor(
int corePoolSize,核心线程数量
int maximumPoolSize,最大线程数量
long keepAliveTime,
TimeUnit unit,非核心线程闲置时间
BlockingQueue<Runnable> workQueue) {任务队列
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}*/
/**
* 当有任务提交到线程池,会先判断核心线程数量是否已达到;
* 若未达到核心线程数量,则创建核心线程执行任务;
* 若已达到核心线程数量,则把任务放到任务队列中;
* 若任务队列已满,而未达到最大线程数量,则创建非核心线程执行任务;
* 若任务队列已满,也已达到最大线程数量,则拒绝执行任务
*/
ThreadPoolExecutor executor = new ThreadPoolExecutor(
3,//核心线程数量
10,//最大线程数量
3,
TimeUnit.SECONDS,//非核心线程闲置最大时间
new ArrayBlockingQueue<>(4));//任务队列
for (int i = 0; i < 100; i++) {
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行任务");
}
});
}
}
private static void method1() {
// 创建线程数量固定的线程池
// ExecutorService service = Executors.newFixedThreadPool(2);
// 创建只有单个线程的线程池
// ExecutorService service = Executors.newSingleThreadExecutor();
// 创建线程数量可伸缩的线程池
//ExecutorService service = Executors.newCachedThreadPool();
// 创建执行延时任务|定时任务的线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/*service.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行任务");
}
},2, TimeUnit.SECONDS);*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行任务");
}
},2,4,TimeUnit.SECONDS);
// 把任务交给线程池 submit()*** execute()
/*for (int i = 0; i < 2; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行任务");
}
});
}*/
// 关闭线程池
// service.shutdown();
}
}