线程基础之notify和wait的异同

同步代码出现的问题 IllegalMonitorStateException

在这里插入图片描述
在这里插入图片描述

在学习多线程的过程中,接触到wait

wait 是来自Object类,
sleep 是指Thread类,这里的sleep是谁调用,谁睡眠
如果你定义A线程,A线程下面有B,A调用B的sleep方法,A睡眠;

sleep不会释放锁
wait可以释放锁,进入线程等待池等待,使得其他线程可以使用同步控制块或者方法。

  • Thread.Sleep(0) 的作用是“触发操作系统立刻重新进行一次CPU竞争”。

java.lang.IllegalMonitorStateException是在调用object的wait和notify,notifyAll方法的时候可能会出现的异常

在调用上述三个方法的时候,线程必须获得该对象的对象级别锁,换句话说,出现这个异常的原因是因为,调用wait和notify,notifyAll的对象没有在同步方法(synchronized修饰的方法)或者同步代码块(synchronized(x){})中。

第一:必须要在被synchronized关键字控制的同步代码块中,才能调用这些方法。
第二,调用者必须为你当前的锁对象。

class DemoOne12345 {

    public static void main(String[] args) {
        Object object = new Object();
        ThreadImpl thread = new ThreadImpl(object);
        ThreadImpl thread1 = new ThreadImpl(object);
        //  开启一个线程
        thread1.start();
        thread.start();
    }
}

//直接调用wait()方法,此时实际上是this.wait(),实际调用者为ThreadImp的实例对象thread。
// 而在修改后的代码中,wait()方法的调用者为lock。
class ThreadImpl extends Thread {

    //  线程锁对象
    private Object lock;

    public ThreadImpl(Object lock) {
        this.lock = lock;
    }

    //  重写线程的run方法,线程的业务执行逻辑
    @Override
    public void run() {

        synchronized (lock) {

            //	执行一个for循环,打印0~9之间的数字
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "当前数字为:" + i);
            }

            try {
                /**	调用wait方法控制线程
                    如果是this.wait说明线程本身并没有
                 
                 调用object的wait方法,是指,你的锁对象object在管理这些线程,因此在调用过程中,
                 调用这应该是object对象,就是你的说对象
                */
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.notifyAll();
            }

        }
    }
}


class DemoOne12345 {

    public static void main(String[] args) {
        Object object = new Object();
        ThreadImpl thread = new ThreadImpl(object);
        ThreadImpl thread1 = new ThreadImpl(object);
        //  开启一个线程
        thread1.start();
        thread.start();
    }
}

//直接调用wait()方法,此时实际上是this.wait(),实际调用者为ThreadImp的实例对象thread。
// 而在修改后的代码中,wait()方法的调用者为lock。
class ThreadImpl extends Thread {

    //  线程锁对象
    private Object lock;

    public ThreadImpl(Object lock) {
        this.lock = lock;
    }

    //  重写线程的run方法,线程的业务执行逻辑
    @Override
    public void run() {

        synchronized (this) {

            //	执行一个for循环,打印0~9之间的数字
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "当前数字为:" + i);
            }

            try {
                /**	调用wait方法控制线程
                    如果是this.wait说明线程本身并没有

                 调用object的wait方法,是指,你的锁对象object在管理这些线程,因此在调用过程中,
                 调用这应该是object对象,就是你的说对象
                */
                this.wait(1000);
            } catch (Exception e) {
                System.out.println(e);
                e.printStackTrace();
            }finally {
                this.notifyAll();
            }

        }
    }
}

  • sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

分析下 this锁和object锁

如果要调用某个对象的wait方法,那么必须这么调用

  • 这里咱们接着说
    已经知道了只有在同步方法块中才可以使用对应的wait方法
    这样就到了如何正确使用对用的锁
    现在这个维度,锁有两种:类锁和对象锁
    下面的第一个得到的是lock锁,就是指的当前的对象锁
    而第二个方法锁的是lock,但是却想让这个类去等待,显然 是不行的
// 正确
Object lock = new Object();
synchronized(lock){
    lock.wait();
}

 
// 错误
Object lock = new Object();
synchronized(lock){
    this.wait();
}

分析一下,对于单个object,我们new多个线程,去操作一个object,这里的synchronized锁住object,那么,这些线程需要先得到这个object‘的锁,就是object锁

// 正确
Object lock = new Object();
synchronized(lock){
    lock.wait();
}

分析一下,对于this,我们new多个线程,去操作这个类,这里的synchronized锁住this,那么,这些线程需要先得到这个类的锁,就是this锁

// 正确
synchronized(lock){
    this.wait();
}

这里继续更新,关于类锁,可以通过一下三种方式实现

  • 说白了,也是两种:静态资源加锁 / 对当前类 .class 属性加锁

给出几个case,关于顺序打印问题

class TestDemp{
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        SolutionTempDempOne1 s = new SolutionTempDempOne1(object);
        new Thread(()->{
            try {
                s.sy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "cur1").start();
        new Thread(()->{
            try {
                s.sy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"cur2").start();


    }
}


class SolutionTempDempOne1 {


    private Object object;

    public SolutionTempDempOne1(Object object){
        System.out.println("new object: " + Thread.currentThread().getName());
        this.object = object;
    }



    public  void sy() throws InterruptedException {

        synchronized(object){
            try{
                object.wait(2000);
                System.out.println("currentThread"+Thread.currentThread().getName());
                System.out.println("this is 123");
            }catch (Exception e){
                System.out.println(e);
            }finally {
                object.notifyAll();
            }
        }

    }

}

对象锁和类锁再次case

这里的case给出的是另一种情况,顺序增加数;

给出不加锁:

class ThreadNoStaticDemo{
    private static Integer num = 0;
    private static Object lock = new Object();


    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                ThreadNoStaticDemo s = new ThreadNoStaticDemo();
                try {
                    s.addNum();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"curThread " + i).start();
//            System.out.println(ThreadNoStaticDemo.num);
        }
//        System.out.println(ThreadNoStaticDemo.num);

    }
    public int getNum(){
        return num;
    }

    public void addNum() throws InterruptedException {
//         synchronized(lock) {
            num++;
//             Thread.sleep(200);
             System.out.println(Thread.currentThread().getName() + ": " + num);
//        }
    }
}

这里的结果不仅仅不等于10,而且不是顺序打印。
在这里插入图片描述

下面是静态加锁:

class ThreadNoStaticDemo{
    private static Integer num = 0;
    private static Object lock = new Object();


    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                ThreadNoStaticDemo s = new ThreadNoStaticDemo();
                try {
                    s.addNum();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"curThread " + i).start();
//            System.out.println(ThreadNoStaticDemo.num);
        }
 //         System.out.println(ThreadNoStaticDemo.num);

    }
    public int getNum(){
        return num;
    }

    public void addNum() throws InterruptedException {
         synchronized(lock) {
            num++;
             Thread.sleep(200);
             System.out.println(Thread.currentThread().getName() + ": " + num);
        }
    }
}

结果可以看出,由于操作的静态对象
其已经被锁住,所以每个时刻的锁释放后才可以被获得。
在这里插入图片描述

class ClassLock {

    private static Object lock = new Object();


    public static void main(String[] args){
        for (int i = 0; i < 5; i++) {
            new Thread(()->{
                ClassLock classLock = new ClassLock();
                // 方式 1
                try {
                    classLock.lockStaticObjectField();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//                System.out.println(Thread.currentThread().getName() + ": starting!");
            },"Thread"+i).start();

        }
    }

    /**
     * 锁住静态变量
     * @throws InterruptedException
     */
    public void lockStaticObjectField() throws InterruptedException{
        synchronized (lock){
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(2*1000);
        }
    }
}
class TestDemp{
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        SolutionTempDempOne1 s = new SolutionTempDempOne1(object);
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                try {
                    s.sy();
                } catch (InterruptedException e) {
                }
            }, "cur"+ i).start();
        }

    }
}


class SolutionTempDempOne1 {


    private Object object;

    public SolutionTempDempOne1(Object object){
        System.out.println("new object: " + Thread.currentThread().getName());
        this.object = object;
    }



    public  void sy() throws InterruptedException {

        synchronized(object){
            try{
                object.wait(2000);
                System.out.println("currentThread"+Thread.currentThread().getName());
                System.out.println("this is 123");
            }catch (Exception e){
                System.out.println(e);
            }finally {
                object.notify();
            }
        }

    }

}

间隔顺序打印


class TestDemp{
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        SolutionTempDempOne1 s = new SolutionTempDempOne1(object);
        for (int i = 0; i < 10; i++) {
            s.sy();
//            new Thread(()->{
//                try {
//                    s.sy();
//                } catch (InterruptedException e) {
//                }
//            }, "cur"+ i).start();
        }

    }
}


class SolutionTempDempOne1 {


    private Object object;

    public SolutionTempDempOne1(Object object){
        System.out.println("new object: " + Thread.currentThread().getName());
        this.object = object;
    }



    public synchronized void sy() throws InterruptedException {

        try{
            this.wait(2000);
            System.out.println("currentThread"+Thread.currentThread().getName());
            System.out.println("this is 123");
        }catch (Exception e){
            System.out.println(e);
        }finally {

        }

    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值