• JavaSE之多线程高级(死锁、线程等待和唤醒)


    一级目录

    在多线程程序中,使用了多把锁,造成线程之间相互等待锁,程序无法向下执行。

    容易产生死锁的条件

    1.有多把锁

    2.有多个线程

    3.有同步代码块嵌套

    注意:开发多线程程序时,应该尽量避免三种条件同时出现在代码中,避免造成线程死锁。

    //制造两个锁
        private static Object o1 = new Object();
        private static Object o2 = new Object();
        public static void main(String[] args) {
    
            //制造死锁
            //两个线程互相持有对方需要的锁,进入互相等待的线程
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (o1) {
                        System.out.println("线程1获得锁1");
                        synchronized (o2) {
                            System.out.println("线程1获得锁2");
                        }
                    }
                }
            });
    
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (o2) {
                        System.out.println("获得锁2");
                        synchronized (o1) {
                            System.out.println("获得锁1");
                        }
                    }
                }
            });
    
            thread1.start();
            thread2.start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    线程等待和唤醒的方法

    等待和唤醒,指的是两个线程或多个线程之间的互相等待或唤醒

    Java中提供了几个方法可以实现线程的等待和唤醒,来自Object类

    方法名说明
    void wait()让当前线程释放锁并进入等待,直到其他线程调用锁的notify()或notifyAll()方法
    void notify()唤醒正在等待对象监视器(锁)的单个线程
    void notifyAll()唤醒正在等待对象监视器(锁)的所有线程
    //wait和notify都是Object提供的
        private static Object lock = new Object();
        public static void main(String[] args) throws InterruptedException {
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程1开始");
                    synchronized (lock) {
                        try {
                            lock.wait();//任何对象都可以调用,前提是这个对象必须作为锁,去调用wait和notify才不会报错
                            //释放锁并等待,能继续执行的条件:1.其他线程调用notify  2.重新抢到锁
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程1结束");
                }
            });
            thread1.start();
    
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程2开始");
                    synchronized (lock) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程2结束");
                }
            });
            thread2.start();
    
            Thread.sleep(100);
    
            Thread thread3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程3开始");
                    synchronized (lock) {
                        lock.notify();//如果多个线程在等待,只能随机唤醒一个线程
    
                        lock.notifyAll();//唤醒所有在等待的线程
                    }
                    System.out.println("线程3结束");
                }
            });
    
            thread3.start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    案例:

    private static Object lock = new Object();
    
        private static int a = 1;
    
        public static void main(String[] args) {
            //奇数线程
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        while (a<=100){
                            if(a%2!=0){
                                System.out.println("奇数线程:"+a);
                                a++;
                                lock.notify(); //唤醒偶数线程去执行
    
                            }else {
                                //是偶数,等待其他线程去打印
                                try {
                                    lock.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            });
    
            //偶数线程
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (lock) {
                        while (a<=100){
                            if(a%2==0){
                                System.out.println("偶数线程--:"+a);
                                a++;
                                lock.notify(); //唤醒奇数线程去执行
    
                            }else {
                                try {
                                    lock.wait(); //是奇数,等待其他线程去执行
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }
            });
    
            t1.start();
            t2.start();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    线程状态

    Thread.State枚举类中有6中线程状态

    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,
    
            /**
             * 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:
             * 
      *
    • {@link Object#wait() Object.wait} with no timeout
    • *
    • {@link #join() Thread.join} with no timeout
    • *
    • {@link LockSupport#park() LockSupport.park}
    • *
    * *

    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: *

      *
    • {@link #sleep Thread.sleep}
    • *
    • {@link Object#wait(long) Object.wait} with timeout
    • *
    • {@link #join(long) Thread.join} with timeout
    • *
    • {@link LockSupport#parkNanos LockSupport.parkNanos}
    • *
    • {@link LockSupport#parkUntil LockSupport.parkUntil}
    • *
    */
    TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    在这里插入图片描述
    wait和sleep的区别:

    1.sleep不释放锁,wait会释放锁

    2.sleep休眠时间到就会自动醒来,wait()会无限等待,直到其他线程notify他,醒来后要重新竞争锁。

    3.sleep()是Thread类的静态方法,wait()是Object类的方法,wait方法需要在同步机制使用锁调用。

    线程池

    线程池是一个容器,可以保存一些长久存活的线程对象,负责创建、复用、管理线程。

    以前写多线程的弊端:

    1.用到线程的时候就创建。

    2.用完之后线程消失。

    解决办法:

    Java提供了线程池技术,让线程可以重复利用,解决线程频繁创建和销毁的问题,提高运行效率。

    线程池的优势:

    提高响应速度,减少了创建新线程的时间。

    降低资源消耗,重复利用线程中线程,不需要每次都创建、销毁。

    便于线程管理,线程池可以集中管理并发线程的数量。

    创建线程池的方式

    因为原本创建线程较为麻烦,有5个参数的,有7个参数的。

    我们使用工具类创建线程池

    Executors类是JDK提供的工具类,可以快速创建线程池。

    方法说明
    static ExecutorService newFixedThreadPool(int nThreads)创建一个线程池,参数为池中线程数

    Callable

    public interface Callable<V> {
        /**
         * Computes a result, or throws an exception if unable to do so.
         *
         * @return computed result
         * @throws Exception if unable to compute a result
         */
        V call() throws Exception;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //Callable:泛型 V就是线程执行完的返回值
    public class MyCall implements Callable<Integer> {
        @Override//和run方法一样,都是线程去执行的任务
        public Integer call() throws Exception {
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    public static void main(String[] args) throws ExecutionException, InterruptedException {
            MyCall myCall = new MyCall();
            ExecutorService pool = Executors.newFixedThreadPool(3);
    
            //提交Callable任务,返回值封装在Future,这样让线程可以继续执行,让future去等待
            Future<Integer> future = pool.submit(myCall);
    
            //从Future对象获取返回值
            //如果线程没执行完,get方法会一直阻塞,直到线程返回结果。
            Integer result = future.get();
            System.out.println(result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Runable和Callable的区别:

    1.Callable的call方法有返回值,Runnbale的run方法无返回值。

    2.call方法可以抛任意异常,run方法不能抛编译异常。

    最后

    如果你对本文有疑问,你可以在文章下方对我留言,敬请指正,对于每个留言我都会认真查看。

  • 相关阅读:
    linux安装配置zeppein
    基于雪花算法的增强版ID生成器
    新技术应用塑造未来景展望
    Python 数据分析与挖掘(一)
    C函数速查手册
    网络安全(黑客)自学
    深度学习模型的参数、计算量和推理速度统计
    设计模式——抽象工厂模式
    数据仓库DW-理论知识储备
    麦芽糖-聚乙二醇-顺铂 cisplatin-PEG-maltose
  • 原文地址:https://blog.csdn.net/weixin_47543906/article/details/127800621