Java——多线程高并发系列之wait()、notify()、notifyAll()、interrupt()

简介: Java——多线程高并发系列之wait()、notify()、notifyAll()、interrupt()

文章目录:


写在前面

Demo1(不在同步代码块中调用wait方法,则产生java.lang.IllegalMonitorStateException 运行时异常)

Demo2(调用wait方法会使执行当前代码的线程进入等待状态)

Demo3notify方法会唤醒之前执行wait方法等待的线程)

Demo4wait() & notify()

Demo5interrupt()

Demo6notifyAll()

Demo7wait(long)

Demo8notify方法唤醒过早,可能会打乱程序正常的执行逻辑)

Demo9notify方法如果唤醒过早,那就不需要让线程继续等待了)

Demo10wait等待条件发生了变化)

写在前面


首先需要说一下:wait()notify()notifyAll()这三个方法并不是线程类中的方法,而是Object类中的方法,也就是说每个对象都有这三个方法。而interrupt()才是线程类中的方法。

Object 类中的 wait()方法可以使执行当前代码的线程等待,暂停执行,直到接到通知或被中断为止.

注意: wait()方法只能在同步代码块中由锁对象调用。调用 wait()方法,当前线程会释放锁。

Object 类的 notify()可以唤醒线程,该方法也必须在同步代码块中由用。没使 wait()/notify() IlegalMonitorStateExeption 异常。如果有多个等待的线程,notify()方法只能唤醒其中的一个,在同步代码块中调用notify()方法后,并不会立即释放锁对象,需要等当前同步代码块执行完后才会释放锁对象,一般将 notify()方法放在同步代码块的最后。

下面我给出自己写好的一些代码案例,来帮助大家更好的理解这四个方法的使用,因为代码中已经包含了必要的注释信息,所以每个Demo下面我就只附上代码和运行结果图了。。。

Demo1(不在同步代码块中调用wait方法,则产生java.lang.IllegalMonitorStateException 运行时异常)


package com.szh.wait;
/**
 * wait()方法是Object类中的方法,是每一个对象都具有的方法
 * 调用wait()方法的代码块必须放在同步代码块中
 * 否则会产生 java.lang.IllegalMonitorStateException 异常
 */
public class Test01 {
    public static void main(String[] args) {
        String test="szh";
        try {
            test.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


Demo2(调用wait方法会使执行当前代码的线程进入等待状态)


package com.szh.wait;
/**
 * wait()方法会使调用它的线程进行等待
 * test对象调用了wait()方法,那么synchronized同步代码块中的锁对象就应该是test
 * 否则程序会产生运行时异常 java.lang.IllegalMonitorStateException
 */
public class Test02 {
    public static void main(String[] args) {
        String test="szh";
        System.out.println("同步前的代码...");
        synchronized (test) {
            System.out.println("同步代码块开始...");
            try {
                //调用wait方法后,当前线程就会等待,同时释放test锁对象
                //当前线程需要被唤醒,如果没有唤醒就会一直等待
                test.wait();
                System.out.println("wait方法后面的代码...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("main线程后面的代码...");
    }
}

Demo3(notify方法会唤醒之前执行wait方法等待的线程)


package com.szh.wait;
/**
 * 需要notify()方法唤醒等待的线程
 */
public class Test03 {
    public static void main(String[] args) {
        String test="szh";
        //wait()
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (test) {
                    System.out.println("线程t1开始等待:" + System.currentTimeMillis());
                    try {
                        //t1线程这里执行wait方法之后,会立刻释放它占有的test对象的锁
                        test.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程t1结束等待:" + System.currentTimeMillis());
                }
            }
        });
        //定义t2线程,负责唤醒t1线程
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (test) {
                    System.out.println("线程t2开始唤醒:" + System.currentTimeMillis());
                    //t2线程这里执行notify方法之后,并不会立刻释放它占有的test对象的锁,而是等到同步代码块执行完才会释放
                    test.notify();
                    System.out.println("线程t2结束唤醒:" + System.currentTimeMillis());
                }
            }
        });
        t1.start(); //开启t1线程,t1开始等待
        try {
            Thread.sleep(1000 * 3); //main主线程睡眠3秒,确保t1进入等待状态
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start(); //t1线程开启3秒后,再开启t2线程,让它唤醒t1线程
    }
}

Demo4(wait() & notify()


package com.szh.wait;
import java.util.ArrayList;
import java.util.List;
/**
 * wait() & notify()
 */
public class Test04 {
    public static void main(String[] args) {
        //定义一个List集合存储String数据
        List<String> list=new ArrayList<>();
        //定义第一个线程,当list集合中的元素数量不等于5时,该线程等待
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (list) {
                    if (list.size() != 5) {
                        System.out.println("线程t1开始等待:" + System.currentTimeMillis());
                        try {
                            //此时线程t1进入等待状态,会立刻释放它占有的list锁对象,然后线程t2执行
                            list.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("线程t1被唤醒了,结束等待:" + System.currentTimeMillis());
                    }
                }
            }
        });
        //定义第二个线程,想list集合中存储元素,当list集合中元素数量为5时,唤醒t1线程
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (list) {
                    for (int i = 0; i < 10; i++) {
                        list.add("data ---> " + i);
                        System.out.println("线程t2向list集合中添加了第" + (i+1) + "个元素");
                        if (list.size() == 5) {
                            //当向list集合中添加了5个元素之后,其size等于5,这时候线程t2执行唤醒线程t1的操作
                            //但是此时,线程t2并不会立刻释放它占有的list锁对象,而是等这个同步代码块全部执行完毕,才会释放
                            //执行完毕的时候,线程t2已经向list集合中添加了10个元素,此时才会释放list锁对象,之后程序会转到28行执行
                            list.notify();
                            System.out.println("线程t2发出了唤醒线程t1的通知...");
                        }
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
        t1.start();
        try {
            //为了确保t2在t1之后执行,让t1先等待,这里先让main线程睡眠1秒
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

Demo5(interrupt()


package com.szh.wait;
/**
 * interrupt()方法会中断 wait() 的等待
 * wait() 方法的中断会产生 InterruptedException 异常
 * 中断之后,该线程也会释放锁对象
 */
public class Test05 {
    //定义常量作为锁对象
    private static final Object OBJ=new Object();
    public static void main(String[] args) {
        SubThread t=new SubThread();
        t.start();
        try {
            //main主线程睡眠2秒,确保子线程t储与wait等待状态
            Thread.sleep(1000 * 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程t执行中断操作
        t.interrupt();
    }
    static class SubThread extends Thread {
        @Override
        public void run() {
            synchronized (OBJ) {
                System.out.println("begin wait...");
                try {
                    OBJ.wait();
                } catch (InterruptedException e) {
                    System.out.println("wait方法的等待被中断了...");
                }
            }
        }
    }
}

Demo6(notifyAll()


package com.szh.wait;
/**
 * notify() & notifyAll()
 * notify() 一次只能唤醒一个线程,如果有多个等待的线程,只能随机唤醒其中的某一个;
 * 想要唤醒所有等待线程,需要调用 notifyAll()
 */
public class Test06 {
    public static void main(String[] args) {
        Object obj=new Object();
        SubThread t1=new SubThread(obj);
        SubThread t2=new SubThread(obj);
        SubThread t3=new SubThread(obj);
        t1.setName("t1");
        t2.setName("t2");
        t3.setName("t3");
        t1.start();
        t2.start();
        t3.start();
        try {
            Thread.sleep(1000 * 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (obj) {
            /*
                调用一次 notify() 只能唤醒其中的一个线程,其他等待的线程依然处于等待状态,
                对于处于等待状态的线程来说,错过了通知信号,这种现象也称为信号丢失
             */
            //lock.notify();
            //唤醒所有的线程
            obj.notifyAll();
        }
    }
    static class SubThread extends Thread {
        private Object obj;
        public SubThread(Object obj) {
            this.obj = obj;
        }
        @Override
        public void run() {
            synchronized (obj) {
                System.out.println(Thread.currentThread().getName() + " ---> begin wait...");
                try {
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " ---> end wait...");
            }
        }
    }
}

Demo7(wait(long)


package com.szh.wait;
/**
 * wait(long): 带有 long 类型参数的 wait()等待,
 * 如果在参数指定的时间内没有被唤醒,超时后会自动唤醒.
 */
public class Test07 {
    private static final Object OBJ=new Object();
    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (OBJ) {
                    System.out.println("线程t开始等待...");
                    try {
                        //如果3000毫秒内,线程t没有被唤醒,则线程t会自动唤醒
                        OBJ.wait(1000 * 3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程t结束等待...");
                }
            }
        });
        t.start();
    }
}

Demo8(notify方法唤醒过早,可能会打乱程序正常的执行逻辑)


package com.szh.wait;
/**
 * 线程 wait()等待后,可以调用 notify()唤醒线程,
 * 如果 notify()唤醒的过早,在等待之前就调用了 notify() 可能会打乱程序正常的运行逻辑.
 */
public class Test08 {
    public static void main(String[] args) {
        final Object Lock=new Object();
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (Lock) {
                    System.out.println("begin wait...");
                    try {
                        Lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("end wait...");
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (Lock) {
                    System.out.println("begin notify...");
                    Lock.notify();
                    System.out.println("end notify...");
                }
            }
        });
        //如果先开启 t1 线程,再开启 t2 线程, 大多数情况下, t1 先等待, 之后 t2 再把 t1 唤醒,程序正常执行
        //t1.start();
        //t2.start();
        //如果先开启 t2 通知线程, 再开启 t1 等待线程, 可能会出现 t1 线程一直等待(因为t2线程已经唤醒过了,不会再次唤醒了),
        //所以t1线程就没有收到 t2 线程唤醒它的通知,t1线程就会一直等待下去
        t2.start();
        t1.start();
        //调用start方法的顺序不一定就是线程实际开启的顺序
    }
}

Demo9(notify方法如果唤醒过早,那就不需要让线程继续等待了)


package com.szh.wait;
/**
 * notify()通知过早, 就不让线程继续等待了
 */
public class Test09 {
    static boolean isFirst=true;
    public static void main(String[] args) {
        final Object Lock=new Object();
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (Lock) {
                    while (isFirst) { //当线程t1是第一个开启的线程时,就等待
                        System.out.println("begin wait...");
                        try {
                            Lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("end wait...");
                    }
                }
            }
        });
        Thread t2=new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (Lock) {
                    System.out.println("begin notify...");
                    Lock.notify();
                    System.out.println("end notify...");
                    isFirst=false; //线程t2通知后,就把第一个线程的标志修改为false
                }
            }
        });
        //如果先开启 t1 线程,再开启 t2 线程, 大多数情况下, t1 先等待, 之后 t2 再把 t1 唤醒
//        t1.start();
//        t2.start();
        //如果先开启 t2 通知线程, 再开启 t1 等待线程, 可能会出现 t1 线程一直等待,没有收到 t2 线程唤醒它的通知
        t2.start();
        t1.start();
        //调用start方法的顺序不一定就是线程实际开启的顺序
    }
}

Demo10(wait等待条件发生了变化)


package com.szh.wait;
import java.util.ArrayList;
import java.util.List;
/**
 * wait 条件发生变化
 * 定义一个集合
 * 定义一个线程向集合中添加数据, 添加完数据后通知另外的线程从集合中取数据
 * 定义一个线程从集合中取数据, 如果集合中没有数据就等待
 */
public class Test10 {
    public static void main(String[] args) {
        //定义添加数据的线程对象
        ThreadAdd threadAdd=new ThreadAdd();
        threadAdd.setName("threadAdd");
        //定义取数据的线程对象
        ThreadSubtract threadSubtract=new ThreadSubtract();
        threadSubtract.setName("threadSubtract");
        //测试一:先开启添加数据的线程,再开启取数据的线程,大多数情况下会正常的存取数据
//        threadAdd.start();
//        threadSubtract.start();
        //测试二:先开启取数据的线程,再开启添加数据的线程,取数据的线程会先等待,等到添加数据之后,再取数据
//        threadSubtract.start();
//        threadAdd.start();
        //测试三: 开启两个取数据的线程,再开启添加数据的线程,此时运行会产生 java.lang.IndexOutOfBoundsException 异常
        ThreadSubtract threadSubtract2 = new ThreadSubtract();
        threadSubtract2.setName("threadSubtract2");
        threadSubtract.start();
        threadSubtract2.start();
        threadAdd.start();
    }
    //1.定义一个集合
    static List<String> list=new ArrayList<>();
    //2.定义方法,从集合中取数据
    public static void subtract() {
        synchronized (list) {
            while (list.size() == 0) {
                System.out.println(Thread.currentThread().getName() + " begin wait...");
                try {
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " end wait...");
            }
            Object obj=list.remove(0);
            System.out.println(Thread.currentThread().getName() + "从集合中取了" + obj + "后,集合中数据的数量:" + list.size());
        }
    }
    //3.定义方法,向集合中添加数据
    public static void add() {
        synchronized (list) {
            list.add("data");
            System.out.println(Thread.currentThread().getName() + "向集合中存储了一个数据");
            list.notifyAll();
        }
    }
    //4.定义线程类调用 add() 取数据的方法
    static class ThreadAdd extends Thread {
        @Override
        public void run() {
            add();
        }
    }
    //5.定义线程类调用 subtract() 方法
    static class ThreadSubtract extends Thread {
        @Override
        public void run() {
            subtract();
        }
    }
}

相关文章
|
1天前
|
Java 调度
|
1月前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
67 9
|
1月前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
39 3
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
25 1
|
2月前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
34 1
|
7月前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
|
6月前
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
533 0
|
4月前
|
监控 算法 Java
企业应用面临高并发等挑战,优化Java后台系统性能至关重要
随着互联网技术的发展,企业应用面临高并发等挑战,优化Java后台系统性能至关重要。本文提供三大技巧:1)优化JVM,如选用合适版本(如OpenJDK 11)、调整参数(如使用G1垃圾收集器)及监控性能;2)优化代码与算法,减少对象创建、合理使用集合及采用高效算法(如快速排序);3)数据库优化,包括索引、查询及分页策略改进,全面提升系统效能。
55 0
|
6月前
|
存储 NoSQL Java
探索Java分布式锁:在高并发环境下的同步访问实现与优化
【6月更文挑战第30天】Java分布式锁在高并发下确保数据一致性,通过Redis的SETNX、ZooKeeper的临时节点、数据库操作等方式实现。优化策略包括锁超时重试、续期、公平性及性能提升,关键在于平衡同步与效率,适应大规模分布式系统的需求。
201 1
|
5月前
|
算法 Java 调度
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之使用Java代码实现令牌桶算法问题如何解决