多线程05

前言

前面我们说到了死锁以及线程可见性的问题

我们将线程可见性主要归结于是JVM自身的一个bug

一个线程写一个线程读

会将一直不变的变量优化到直接从寄存器中读取,而不是缓存等读取,因为这样我们就设置了使用volatile关键字使得用到这个变量的时候必须从内存中读取数据

死锁主要是四个原因导致:不可抢占,互斥使用,循环等待,请求保持

其中只有循环等待是最好破坏的,我们可以使用规定线程的加锁顺序来破坏这种循环等待的效果

本节我们将讨论wait和notify两个方法的使用 

为什么引入这两个方法,有什么用?

和join方法类似,这两个方法还是用来在应用层面上规定代码的执行顺序,事实上在操作系统内核中线程的调度仍然是无序的

这里的干预其实就是让某个线程主动放弃了去cpu执行的权利,相当于放弃了被调度的机会

举个例子:

此时a线程想去atm中取钱,b线程和c线程想进去存钱,恰好此时a拿到了锁,进去了atm房间

此时a就可以一直占用atm机器等待,加入发现没钱出来,a仍然在行列中竞争锁,所以a一直能持有锁(概率问题,就像我跟你分手了,我们复合的概率更大一样),此情况就称之为线程饿死,因为其他的线程都拿不到锁,执行不了自己的代码

(就是某个线程一直反复获取锁,但是又不执行实质性的逻辑)

wait做了什么??

wait方法实际上是做了三件事情

1.释放锁

2.阻塞等待

3.当其他线程调用notify方法的时候,解除阻塞状态,持有锁之后继续运行代码

join和notify的区别

join方法是指假如在主线程中调用t1.join

此时主线程就会等t1线程执行完才会继续执行

而wait方法除非有线程去唤醒他,他会一直等到枯树开出花

当然,我们也是可以设置wait的最大时间的,也就是等不到结果就直接不等了

产生阻塞的几种原因

join/wait  BLOCKED

sleep       TIMED_WAITING

synchronized BLOCKED

由于wait是object类中的一个方法,所以随便拿个对象都可以使用wait方法,但是得持有锁才行,不然会发生异常

我们一般在synchronized代码块中使用

注:调用wait的对象一般和synchronized的锁一致

一个简单的使用案例

public static void main(String[] args) {
        //统一对象进行加锁解锁
        Object lock = new Object();
        Thread t1 = new Thread(()->{
            synchronized (lock){
                System.out.println("t1 wait 之前");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("t1 wait 之后");
            }
        });

        Thread t2 = new Thread(()->{
            synchronized (lock){
                try {
                    Thread.sleep(5000);
                    System.out.println("notify之前");
                    lock.notify();
                    System.out.println("notify之后");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
    }

此时的结果就是,t1线程在执行过打印效果后,会进入阻塞状态,然后t2睡眠5000ms之后会执行一次打印动作,然后让t1解除阻塞状态,最后执行完下一次打印后释放锁

注:notify方法只会唤醒阻塞的线程,并不会释放锁

注:wait和notify方法是成套使用的,两者依靠对象联系起来

假如这里我们使用object1对象来wait这个线程

再使用objcet2是唤不醒这个线程的

多线程知识点小结

1.线程的特性,线程和进程的区别

2.Thread类创建线程的几种方式

3.Thread类的一些属性

4.启动线程

5.终止线程

6.等待线程

7.线程休眠

8.获取线程引用

9.线程状态

10.线程安全问题

        10.1 线程安全产生的原因及解决方法

        10.2 死锁问题

        10.3 内存可见性问题

11.线程的wait和notify...

猜你喜欢

转载自blog.csdn.net/qiuqiushuibx/article/details/134709704