线程基础知识

引入

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()必须在synchronized4.wait释放锁,所以可以交替运行
-------------------------------------
wait和sleep在锁上的区别(synchronized1.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();
            }
        }
    }
}

线程高级部分

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值