引入
1.I/O
- BIO:同步阻塞
- NIO:同步非阻塞
- I/O多路复用
- AIO:异步
2.并发与并行
- 并发:两个及两个以上作用在同一时间段内执行
- 并行:两个及两个以上作业在同一时刻运行
3.上下文切换
- 上下文:线程的运行条件与状态
- 当主动让出CPU(sleep(),wwait()),阻塞,时间片用完或终止运行的线程时线程不在占用CPU,前三种都涉及线程切换,需保留当前线程的上下文,待再次占用CPU时,恢复现场并加载下一个占用CPU线程的上下文
- 频繁的切换上下文会消耗CPU
进程与线程
1.系统性能瓶颈
- 耗时
- 假死
- 阻塞
2.问题引入
- 用户输入一个指令,计算机做一个操作,当用户在思考或输入数据时,计算机就在等待 --> 批处理操作系统出现
- 问题1:如果有两个任务A和B,A执行到一半时需要读取大量的数据输入,此时CPU只能静静地等待A读取完数据才能继续执行 --> 白白浪费CPU资源
- 解决1+问题2:内存中装多个程序,当A执行耗时操作时,让CPU执行B,那么问题来了,内存中的多个程序使用的数据如何分辩?一个程序运行暂停后,再次启动时如何恢复到原来的状态?
- 解决2:进程
2.进程
- 进程是程序的一次执行(动态),系统运行某程序即是进程的创建到销毁过程
- 每个进程对应一定的内存地址空间,约定每个进程只能使用自己的内存空间,各进程互不干扰。
- 进程空间中可以保存程序运行的状态,CPU轮询每个进程,当下一次进程重新切换回来时,可以从进程空间中恢复原来的状态
- 进程的缺点:一个进程在一个时间段内只能处理一个任务
- 解决:线程
3.线程
- 一个进程中包含多个线程,每个线程执行一个子任务
- 线程共享此进程的对和方法区资源,每个线程都有自己的程序计数器,虚拟机栈和本地方法栈
- 线程可以在进程内部并发执行
- 时间开销小,不利于资源的分配与管理
- 线程主要解决耗时与阻塞操作
4.进程线程的区别
- 进程是操作系统资源分配的基本单位,线程是操作系统调度的基本单位
- 一个应用程序对应一个进程, 一个进程对应多个线程
- 进程间通信(数据库)非常麻烦, 但线程非常方便
- 进程独享资源, 线程共享它们所属的进程的资源
线程共享进程的堆和方法区,每个线程都有自己的程序计数器,虚拟机栈和本地方法栈
-->为什么程序计数器是线程私有的?
程序计数器主要有两个作用,代码流程控制与记录当前线程执行位置,私有主要是为了线程切换回后恢复到正确的执行位置
-->为什么虚拟机栈和本地方法栈是线程私有的?
二者作用相同,都保存局部变量表等信息,但前者为JVM执行JAVA方法<字节码>服务,后者为JVM运行到的native服务,私有主要是保证线程中的局部变量不被其他线程访问
- 进程间相互独立,线程间可能相互影响
- 进程结束,则这个进程所产生的线程也会销毁
Java中线程的处理方式
1.说明
1.启动线程要start,jvm会自动调用线程中的run()
2.线程先后顺序和CPU调度程序有关,和谁先start无关
3.主线程代码运行完毕,但是子线程还没有运行完毕,所以主线程也没有退出,直到子程序运行完毕
2.继承Thread类
- 外部类
1.MyThread继承Thread,重写run()加入耗时的或阻塞操作
2.缺点:Java是单继承的,影响类的扩展性
------------------------------------------------------------------
public class t1_classExtends {
public static void main(String[] args) throws InterruptedException {
System.out.println("主方法开始");
MyThread1 m1 = new MyThread1();
//设置线程名
m1.setName("线程1--继承Thread外部类--");
//设置优先级(理论上先调用优先级高的) 1-10
m1.setPriority(1);
//子程序运行
m1.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("主方法在运行");
}
}
}
class MyThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//Thread.currentThread().getName():输出当前线程的名字 未给名字系统分配
System.out.println(Thread.currentThread().getName()+"i=" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
------------------------------------------------------------------
主方法开始
线程1--继承Thread外部类--i=0
线程1--继承Thread外部类--i=1
主方法在运行
主方法在运行
线程1--继承Thread外部类--i=2
主方法在运行
线程1--继承Thread外部类--i=3
主方法在运行
线程1--继承Thread外部类--i=4
主方法在运行
线程1--继承Thread外部类--i=5
线程1--继承Thread外部类--i=6
线程1--继承Thread外部类--i=7
......
- 内部类
1.只有某一个类使用时
------------------------------------------------------------------
public class t1_classExtends {
public static void main(String[] args) throws InterruptedException {
System.out.println("主方法开始");
MyThread2 m2 = new MyThread2();
m2.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("主方法在运行");
}
}
static class MyThread2 extends Thread {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("内部类:i=" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2.实现Runnable接口
- 外部类
1.缺点:无法获得新线程的执行结果
------------------------------------------------------------------
public class t2_runnable {
public static void main(String[] args) {
System.out.println("主方法开始");
MyRunnable task = new MyRunnable();
Thread thread1 = new Thread(task, "线程2--实现runnable--");//创建线程对象(任务,线程名)
thread1.setPriority(10);
thread1.start();//启动后,jvm会自动回调他的配置 task中的run()
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+"i=" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 匿名内部类
1.只有某一个地方使用时
------------------------------------------------------------------
public class t2_runnable {
public static void main(String[] args) throws InterruptedException {
System.out.println("主方法开始");
//方法1
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+"i=" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"线程3.1--匿名内部类--");
thread.start();
//方法2
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+"i=" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"线程3.2--匿名内部类--").start();
}
}
- lambda写法(函数式编程方法)
1.底层有注解@FunctionalInterface则支持函数式编程
------------------------------------------------------------------
public class t2_runnable {
public static void main(String[] args) throws InterruptedException {
System.out.println("主方法开始");
//方法1
Thread thread = new Thread( ()->{
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+"i=" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"线程4.1--lambda写法--");
thread.start();
//方法2
new Thread( ()->{
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName()+"i=" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"线程4.2--lambda写法--").start();
}
}
3.实现Callable接口
- 前两种方案都无法获得新线程的执行结果
- FutureTask继承结构
- 代码实现
//实现callable接口 可以有返回值
public class t3_callable {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
//方法1
FutureTask<Integer> futureTask1 = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int count = 0;
for (int i = 0; i <= 100; i++) {
Thread.sleep(100);
count += 1;
}
return count;
}
});
//方法2.lambda表达式
FutureTask<Integer> futureTask2 = new FutureTask<Integer>(() -> {
int count = 0;
for (int i = 0; i <= 100; i++) {
Thread.sleep(100);
count += 1;
}
return count;
});
//方法3 (Runnable,result)
AtomicReference<Integer> result = new AtomicReference<>();
FutureTask futureTask3 = new FutureTask(()-{
result.set(1);
},result);
//result.get()取值
//创建线程 - thread(Runnable) 多态
Thread thread = new Thread(futureTask2);
//启动线程
thread.start();
//1.无限等待get():等到任务执行出现异常或任务执行完毕跳出
//System.out.println("1+2+...+100=" + futureTask2.get());
//2.超时等待get(超时时间,单位):超过时间就报错
//V get(long timeout, TimeUnit unit)
System.out.println("1+2+...+100=" + futureTask2.get(1, TimeUnit.SECONDS));
//get是阻塞式的方法,要等其结果出来之后主线程才会继续,不get就不会阻塞
System.out.println("主程序中其他的代码");
}
}
4.线程池
- 直接创建线程在程序运行过程中创建多少个没办法限定,线程实际会消耗空间,导致内存溢出,虚拟机内存耗尽
- 好处:减少创建和销毁线程消耗的时间以及系统资源的开销,解决资源不足的问题
- 线程池原理
- AtomicInteger原子性原理
public class t4_pool {
public static void main(String[] args) {
//核心线程池的大小,即最开始线程大小,不够则扩充到max
int corePoolSize = 2;
//核心线程池的最大线程数
int maxPoolSize = 4;
//线程最大空闲时间,即10unit没有用就回收
long keepAliveTime = 10;
//时间单位
TimeUnit unit = TimeUnit.SECONDS;
//阻塞队列,容量为2,最多允许放入两个空闲任务
//该线程最做允许6个任务4+2
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
//线程创建工厂
ThreadFactory threadFactory = new NameThreadFactory();
//线程池拒绝策略----任务太多,阻塞队列都存不下
RejectedExecutionHandler handler = new MyIgnorePolicy();
ThreadPoolExecutor executor = null;
try {
//推荐的创建线程池的方式如下,不推荐使用现成的API创建线程池
executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
//预启动所有核心线程,提升效率
executor.prestartAllCoreThreads();
//任务数量
int count = 10;
for (int i = 0; i <= count; i++) {
RunnableTask task = new RunnableTask(String.valueOf(i));
//提交任务到线程池 还有4个任务无法执行
executor.submit(task);
}
} finally {
//断言 可开关 -ea -da
assert executor != null;
//关闭线程池
executor.shutdown();
}
}
//线程工厂
static class NameThreadFactory implements ThreadFactory {
//AtomicInteger原子类(整体):保证复制,更新,复写回的过程是一个整体(并发编程),保证线程安全
//123线程使用线程池时变量要依次添加,而不是一个新线程调用时又从0开始
private final AtomicInteger threadId = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "线程--" + threadId.getAndIncrement());//相当于i++
System.out.println(t.getName() + "已被创建");
return t;
}
}
//线程池拒绝策略
public static class MyIgnorePolicy implements RejectedExecutionHandler {
@Override //被拒绝的对象,线程池对象
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
doLog(r, executor);
}
private void doLog(Runnable runnable, ThreadPoolExecutor e) {
//可以做日志记录等
System.out.println("线程池:" + e.toString() + runnable.toString() + "被拒绝执行");
}
}
//任务类
static class RunnableTask implements Runnable {
private String name;
public RunnableTask(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(this.toString() + " is running!");
Thread.sleep(3000);//让任务慢点执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "RunnableTask{name='" + name + "\'}";
}
}
}
线程中断
1.概述
- 线程中断是线程间的协作机制
- 中断只是标识位属性,只通知而不强制线程立刻退出,中断的线程要根据中断的状态选择合理的处理方式,如检测到线程中断时不做处理,此线程还是可以继续执行
2.线程中断操作
- interrupt() :实现中断
- isInterrupted() :检测中断状态,不清除中断状态
public class t6_isInterrupted {
public static void main(String[] args) {
//当前线程
Thread thread = Thread.currentThread();
//检测当前线程是否被中断
System.out.println(thread.getName() + "线程是否中断:" + thread.isInterrupted());//false
//中断线程
thread.interrupt();
//检测当前线程是否被中断
System.out.println(thread.getName() + "线程是否中断:" + thread.isInterrupted());//true
//检测线程中断状态是否被清除
System.out.println(thread.getName() + "线程是否中断:" + thread.isInterrupted());//true
}
}
- interrupted() :检测中断状态,同时清除中断状态
public class t5_interrupt {
public static void main(String[] args) {
//通过interrupt()方法实现中断
//通过interrupted()方法检测线程是否被中断,清除中断状态
//Thread.currentThread()主线程
System.out.println(Thread.currentThread().getName() + "线程是否中断:" + Thread.interrupted());//false
//设置线程中断
Thread.currentThread().interrupt();//变成中断态
//Thread.currentThread().stop();//过期(强制线程关闭 会很不安全)
//返回true后恢复状态
System.out.println(Thread.currentThread().getName() + "线程是否中断:" + Thread.interrupted());//true
System.out.println(Thread.currentThread().getName() + "线程是否中断:" + Thread.interrupted());//false
}
}
3.响应线程中断的方式
- 抛出 InterruptedException
public class t7_ReThrowInterruptException {
public static void main(String[] args) throws InterruptedException {
//当前线程
Thread thread = Thread.currentThread();
try {
//当前线程中断
thread.interrupt();
//线程休眠3s
Thread.sleep(3000);//中断后无法睡眠
} catch (InterruptedException e) {
System.out.println(thread.getName() + "抛出InterruptedException中断异常");
System.out.println(thread.isInterrupted());//false
//异常处理完毕,不会再中断
}
}
}
- 捕获 InterruptedException后重新抛出
public class t7_ReThrowInterruptException {
public static void main(String[] args) throws InterruptedException {
//当前线程
Thread thread = Thread.currentThread();
try {
//当前线程中断
thread.interrupt();
//线程休眠3s
Thread.sleep(3000);//中断后无法睡眠
} catch (InterruptedException e) {
System.out.println(thread.getName() + "抛出InterruptedException中断异常");
System.out.println(thread.getName() + "做一些清理工作");
//再次抛出异常,异常未处理完毕
throw e;
}
}
}
- 检测到中断后,重新设置线程中断
public class t8_ReInterrupted extends Thread {
public static void main(String[] args) throws InterruptedException {
//当前线程main
String threadName = Thread.currentThread().getName();
//创建线程
t8_ReInterrupted reInterrupted = new t8_ReInterrupted();
System.out.println(printDate() + threadName + "线程启动");
//启动新线程
reInterrupted.start();
//主线程休眠3s
Thread.sleep(3000);
System.out.println(printDate() + threadName + "设置子线程中断");
//对新线程设置线程中断
reInterrupted.interrupt();//睡眠1s被打断
//主线程休眠3s
Thread.sleep(3000);
System.out.println(printDate() + threadName + "运行结束");
}
@Override
public void run() {
//当前线程
String threadName = Thread.currentThread().getName();
int i = 0;
//循环等待线程中断,只要当前线程不是中断态则继续,是中断则退出
//当前线程未中断则会进入循环
while (!Thread.currentThread().isInterrupted()) {
System.out.println(printDate() + threadName + "线程正在执行第 " + (++i) + "次");
try {
//线程阻塞,若线程收到中断操作信号将抛出异常
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(printDate() + threadName + "线程正在执行");
//检测线程是否中断
System.out.println(printDate() + threadName + "的状态:" + this.isInterrupted());//false
//如果需要维护中断状态,则需要重新设置中断状态
Thread.currentThread().interrupt();//true
//此处中断,退出循环
}
}
System.out.println(printDate() + threadName + "线程是否被中断:" + this.isInterrupted());
System.out.println(printDate() + threadName + "线程退出");
}
private static String printDate() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return simpleDateFormat.format(new Date()) + " ";
}
}
线程分类
1.主线程
- 由main方法生成的线程
- 任意Java程序至少有一个线程
2.精灵线程(守护线程)
- 其它线程结束,精灵线程也结束
- 相当于后台线程
- 1个Java程序远不止1个线程,后台有系统启动精灵线程
- setDaemon(true):设置某线程为精灵线程
//精灵线程:其他线程结束,精灵也结束
public class t9_deamon {
public static void main(String[] args) {
Thread thread1 = new CommonThread();
Thread thread2 = new Thread(new MyDeamon());
//设置为守护线程,线程运行前设置守护线程
thread2.setDaemon(true);
thread2.start();//原循环20次,但实际只有5次
thread1.start();//原循环5次
}
}
//线程类
class CommonThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("用户线程:" + i + "执行!");
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//任务类
class MyDeamon implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("守护线程:" + i + "执行!");
try {
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
用户线程:0执行!
守护线程:0执行!
用户线程:1执行!
守护线程:1执行!
用户线程:2执行!
守护线程:2执行!
用户线程:3执行!
守护线程:3执行!
用户线程:4执行!
守护线程:4执行!
守护线程:5执行!
3.子线程
- 与父线程有相同的优先级
线程属性
- id
- name
- priority:优先级(1-10),默认5
1.子线程和父线程有相同的优先级(继承性)
2.优先级越高,获取CPU的概率更高
3.优先级不保证线程的执行顺序,并不是绝对的
线程组 ThreadGroup
1.作用
- 批量管理线程或线程组对象
2.使用
public class t15_ThreadGroup {
public static void main(String[] args) {
TestThread t1 = new TestThread();
TestThread t2 = new TestThread();
ThreadGroup g1 = new ThreadGroup("线程组1");
Thread thread1 = new Thread(g1, t1);
thread1.start();
Thread thread2 = new Thread(g1, t2);
thread2.start();
System.out.println("活动的线程组:" + g1.activeCount());
System.out.println("线程组名称:" + g1.getName());
//线程组中断,则组内所有线程中断
g1.interrupt();
//while循环中sleep异常被捕获,循环终止
}
}
class TestThread implements Runnable {
@Override
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("线程名:" + Thread.currentThread().getName());
Thread.sleep(3000);//两个线程被打断
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
抛出两个异常
函数式编程
1.函数式接口编写
- 函数式接口注解:@FunctionalInterface
- 接口中只有1个抽象方法
不包括与Object的方法重名的方法,从Object继承过来的抽象方法
- 写法
@FunctionalInterface//函数式接口注解:支持函数式操作
interface Action<T> {
void execute(T t);//一个抽象方法
}
public class t11_function{
public static void main(String[] args) {
//1
Action action = System.out::println;//作为函数式接口中抽象方法的execute的实现
action.execute("hello");
//2.回调方式
test(System.out::println, "hello");
//3.新无参无返回值方法替换函数式接口的唯一抽象函数
//相当于sleepMethod替代run
t11_function t1 = new t11_function();
new Thread(t1::sleepMethod).start();
}
static void test(Action action, String str) {
action.execute(str);
}
public void sleepMethod() { .... }
}
线程生命周期
1.图解
2.利用生命周期函数
- synchronized 关键字
对象锁:每个对象一个锁
类别:1.同步代码块:小范围
synchronized(加锁的对象this/类.class){}
2.同步方法:大范围,给对象加锁
3.同步静态方法:大范围,给类加锁
作用:解决多线程操作一个资源时数据不一致的问题(资源争抢,竞态条件)
注意:构造方法不能加锁,构造方法本来就是线程安全的
- 睡眠:Thread.sleep(时间) //毫秒
1.静态方法:Thread.sleep(时间)
2.sleep后线程进入超时等待状态,时间到变为就绪状态,若调度到CPU变为运行否则还是就绪状态
3.可能有异常:try{}catch(){}
InterruptedException(受检异常)
4.interrupt()打断线程休眠状态,打断后catch起作用
5.sleep暂时让出执行权,不释放锁
***由于没有释放锁(对象监视器),因此其它线程也无法获取锁
6.只是线程操作,不涉及到线程间通信(不释放锁)
- wait
1.Object的方法
2.参数 wait()/wait(long timeout)/wait(long timeout,nanos)(超时时间,单位)
3.执行wait(),必须要有锁,所以wait()必须在synchronized中
4.wait释放锁,所以可以交替运行
-------------------------------------
wait和sleep在锁上的区别(synchronized)
1.sleep():CPU未使用,让出执行权,但不会释放锁
线程按照先后顺序执行,,某个线程执行完才会执行下一个
性能很差
2.wait():CPU未使用,让出执行权,同时会释放锁
线程间交替执行,wait后会先执行其他获取到该锁的线程,当再次获得锁时继续执行
- yield
yield使当前线程让出cpu的使用权给相同或更高优先级的线程,但不保证其他线程一定可以获得CPU执行权(意思是没有执行权有可能还需要先执行自己)
-------------------------------------
yield与sleep的区别
1.sleep():让出执行权,不考虑优先级
有异常,可以打断
执行后变为超时等待状态
2.yield():让出使用权,相同或更高优先级的线程
无异常,不可以打断
执行后变为就绪状态
- join
join使该线程立即执行,正在运行的线程先阻塞,待该线程结束后再执行
-------------------------------------
yield与join的区别
1.yield():静态方法
让位给相同或更高优先级的线程
2.join():实例方法(thread对象.join())
不考虑优先级
- notify
1.Object的方法
2.唤醒一个线程
- notifyAll
1.Object的方法
2.唤醒所有等待队列中的线程
死锁
1.概述
- 两个或两个以上的进程在执行过程中,由于竞争资源或彼此通信而造成的阻塞现象,若无外力作用,它们都将无法推进
2.死锁产生的四个条件
- 互斥
某资源每次只能被一个进程使用
==对象锁: 一次只能有一个线程执有
- 请求与保持
进程因请求资源而阻塞时,对已获得的资源保持不放
- 不可剥夺
进程已获得的资源在末使用完之前,不能强行剥夺
- 循环等待
若干进程间头尾相接的循环等待资源关系
3.解决方案
- 先查到死锁位置
- 再任意破坏一个死锁的条件
4.死锁案例
public class t17_deadLock implements Runnable {
public int flag = 1;
//创建两个对象,产生两个对象锁
static Object o1 = new Object();
static Object o2 = new Object();
public static void main(String[] args) {
t17_deadLock td1 = new t17_deadLock();
t17_deadLock td2 = new t17_deadLock();
td1.flag = 1;
td2.flag = 0;
Thread t1 = new Thread(td1);
Thread t2 = new Thread(td2);
t1.start();
t2.start();
}
/**
* 1.互斥:synchronized
* 2.请求与保持:获取o1会一直锁
* 3.不可剥夺:flag=1时获取o1,flag=0是没办法强制剥夺的
* 4.循环等待:两个线程间是循环的关系
*/
@Override
public void run() {
System.out.println("flag=" + flag);
if (flag == 1) {//flag=1表示线程1
synchronized (o1) {//o1加锁
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {//o1里面获取o2的锁
System.out.println("1");
}
}
}
if (flag == 0) {//flag=0表示线程2
synchronized (o2) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("2");
}
}
}
//破坏一个条件即可解决死锁
//两个都先取o1 再取o2
}
}
生产者与消费者
1.图解
2.代码实现
public class productConsumer {
public static void main(String[] args) {
AppleBox ab = new AppleBox();//创建中间件
//注意ab是同一个ab 所以pc使用同一把锁
Producer p = new Producer(ab);
Consumer c = new Consumer(ab);
//Consumer cd = new Consumer(ab);
new Thread(p).start();
new Thread(c).start();
//new Thread(cd).start();
}
}
//待处理的消息
class Apple {
int id;
Apple(int id) {this.id = id;
@Override
public String toString() {
return "Apple" + id;
}
}
//中间件
class AppleBox {
int index = 0;
//将来要考虑扩容问题
Apple[] apples = new Apple[5];
//只有一个生产者可以进来生产
public synchronized void deposite(Apple apple) {
//容器是满的
while (index == apples.length) {//防止index越界,设置大锁在循环之外,否则醒来时不判断index的值
try {
this.wait();//存满了等待,
//没有加时间参数,唤醒才会结束等待,加上时间自己醒就不用别人激活了
//但醒来的时间是不可知的 ,通知别人激活
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();//通知其他线程激活,就算都活了也只有有锁的才可以运行
//wait也可以添加时间,但是该时间是不可预知的,最好还是统一唤醒
apples[index] = apple;
index++;
}
//只有一个消费者可以进来消费
public synchronized Apple withdraw() {
//容器是空的
while (index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return apples[index];
}
}
class Producer implements Runnable {
AppleBox ab = null;//生产和消费对应的资源是同一个
Producer(AppleBox ab) {this.ab = ab;}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
Apple a = new Apple(i);
ab.deposite(a);
System.out.println(Thread.currentThread().getName() + "生产了" + a);
try {
Thread.sleep((int) (Math.random() * 1000));//生产消耗一点时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
AppleBox ab = null;//生产和消费对应的资源是同一个
Consumer(AppleBox ab) {this.ab = ab;}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
Apple a = ab.withdraw();
System.out.println(Thread.currentThread().getName() + "消费了" + a);
try {
Thread.sleep((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}