javaEE多线程(三)---线程安全(二)

目录

1.volatile(翻译为:易变的,可变性的,无定性的)

2.两种模式:

3.多线程的两种模式

代码实例:(面试考)

4.线程通知(wait(),notify())

5.阻塞队列------BlockingQueue(来自queue的子接口)

6.自己实现的阻塞队列------ArrayBlockingQueue(循环队列)

7.定时器(类)

7.1定时器的使用

7.2自己实现一个定时器(重点)

7.2优化版本的定时器 

8.面试题:sleep和wait的区别

 9.线程池

9.2 为什么使用线程池?

9.3Java中的TreadPoolExecutor的构造方法+创建流程。

9.4线程池

9.5 自己实现一个线程池(这里不太懂)

1.volatile(翻译为:易变的,可变性的,无定性的)

用来修饰变量。保证JVM中线程要读变量,每次从主内存读,保证每次写,写回主内存

功能:(经常改变的变量用volatile修饰)

volatile不可修饰局部变量,没有意义。

a.90%是用来保护内存可见性的(最重要的作用)

b.也可用来保护原子性:long、double修饰的属性(64位)不是原子的,所以要保护它的原子性,要加上volatile。

long a=100;   //不是原子的

double a=100;//不是原子的

volatile long a=100; //原子的

volatile double a=100;//原子的

javaEE多线程(三)---线程安全(二)_第1张图片

c.代码的重排序

加上volatile SomeObject so;可以防止代码的重排序。(就不会把她重排序)

手里拿着钥匙(引用)可以去装修房子,拿着毛胚房没用

javaEE多线程(三)---线程安全(二)_第2张图片

回顾:判断是否需要保证原子性的两个特点

1.读写操作

a++

size++

2.check update操作

if(...){...}

2.两种模式:

1.单例模式(singleton pattern)

通过代码,保护一个类,使得类在整个进程(应用)运行过程中,有且只有一个对象。

(java中写的图书管理系统)

2.设计模式 

对一些解决通用问题的,经常书写代码片段的总结与归纳(因为多次用到所以写一个方法)

3.多线程的两种模式

饿汉模式:一开始就初始化

懒汉模式:等到用的时候才初始化(Arrays链表、HashMap都是用到来初始化)

代码实例:(面试考)

author wangqi
 * @data 2022/2022/4/25/251915
 * 懒汉模式3
 */
public class LazyModeV3 {
    private volatile static LazyModeV3 instance = null; //避免重排序

    public static LazyModeV3 getInstance() {
        // 第一次调用这个方法时,说明我们应该实例化对象了
        if (instance == null) {
            //只有instance 还没有初始化时,才会走到这个分支
            //这里没有锁保护,所以理论上可以有很多线程同时走到这个分支
            synchronized (LazyModeV3.class) {//如果俩个线程进来了,也只有一个线程会加到锁,然后在判断是否为空
               //通过上面的条件,让争抢锁的动作旨在instance
                //实例化之前才可能发生,实例化之后就不再发生


               //加锁之后才能执行
                //第一个抢到锁的线程,看到的instance是null
                //其他抢到锁的线程,看到的instance不是null
                //保证了instance,只会被实例化一次
                if (instance == null) {
                    instance = new LazyModeV3();    // 只在第一次的时候执行
                    //上面一行,当重排序成1-3-2时候可能出问题
                    //通过volatile修复
                }
            }
        }

        return instance;
    }

    private LazyModeV3() {}
}

4.线程通知(wait(),notify())

wait()、notify()方法属于Object类,Java方法都有这俩个方法

要想使用wait和notify,必须对'对象'进行synchronixed加锁

javaEE多线程(三)---线程安全(二)_第3张图片

 wait的中止条件有:

1.线程被通知唤醒了

2.线程被中止了(异常)

3.假唤醒(wait醒来的时候条件没有被满足,也被唤醒了)

4.超时时间到达

notify唤醒规则:

1.随机唤醒,notifyAll全部唤醒

2.wait-notify是没有状态保存的,必须先wait后notify

如果先notify后wait,wait无法感知之前曾有过notify,会永远等待下去。

wait(包含重载形式)

notify(包含notifyAll)

wait()之后只会释放wait的这把锁

Object类,所有对象都有。

而Condition(Lock锁)和wait、notify的功能一模一样。

javaEE多线程(三)---线程安全(二)_第4张图片

现在更推荐Lock锁相比于synchronized锁

wait、notify天生于sync绑定

5.阻塞队列------BlockingQueue(来自queue的子接口)

阻塞队列:我取不到东西的时候,我就一直等在那里

 put(e) 队列满的情况下放就会阻塞,当有人让线程结束时,放入失败了,也会结束、take();

javaEE多线程(三)---线程安全(二)_第5张图片

还有超时的:

javaEE多线程(三)---线程安全(二)_第6张图片

6.自己实现的阻塞队列------ArrayBlockingQueue(循环队列)

 一个生产者和一个消费者的问题

生产-消费者模型

生产者:一个(多个线程),只负责向队列放入元素

消费者:一个(多个线程),只负责从队列取出元素

线程和线程之间需要互相等待和通知wait()、notify();

7.定时器(类)

定时器执行任务时,不会占用我们的当前执行流。

Timer类------任务调度作用(闹钟)

abs TimerTask(本身是抽象类)-----继承这个类,重写run方法即可

模式:

多少秒之后执行什么任务。

周期性的执行任务

7.1定时器的使用

javaEE多线程(三)---线程安全(二)_第7张图片

 javaEE多线程(三)---线程安全(二)_第8张图片

7.2自己实现一个定时器(重点)

一个延时任务,需要创建一个线程

java中的Timer实现的方式:一个任务,执行多个任务

创建一个工作线程:用优先级阻塞队列,当有很多任务让Timer去执行的时候,把任务放入优先级队列,然后创建一个死循环不断的从优先级队列中去任务执行。

问题:先执行哪个任务?

1.delay时间最小的任务。

其实定时器也是个生产者消费者线程

7.2优化版本的定时器 

多次执行,执行周期性任务

如果有新线程进来,会唤醒所有任务,然后再比较看现在谁应该被执行

javaEE多线程(三)---线程安全(二)_第9张图片

 掌握第一个版本即可。

8.面试题:sleep和wait的区别

a.语义不同:休眠VS等待

b.sleep()一定固定休眠时间,wait(timeout)有俩个结束条件:超时时间已到or条件满足。

c.sleep是静态方法

object()普通方法

d.sleep和锁没关系,

wait是释放当前的锁。

 9.线程池

9.2 为什么使用线程池?

因为线程的创建和销毁都需要消耗一定的成本。

线程池模式:

提前创建好很多线程(按需创建)

来了新任务交给储备的线程处理。

处理完了让其重新回到空闲的状态

9.3Java中的TreadPoolExecutor的构造方法+创建流程。

构造方法:

9.4线程池

也是生产者消费者模型

javaEE多线程(三)---线程安全(二)_第10张图片

service.execute方法把task任务传入阻塞队列里面 

TreadPoolExecutor(按照按需创建的过程)

1.一开始,线程池里一个工作线程也没有

2.随着任务的提交

//当队列已满时,才会创建新线程

javaEE多线程(三)---线程安全(二)_第11张图片

 

构造方法参数:

javaEE多线程(三)---线程安全(二)_第12张图片

Executor(接口)

Executors(定义了一些固定策略的线程池) 

问题 

9.5 自己实现一个线程池,我写的是线程不安全的(这里不太懂)

javaEE多线程(三)---线程安全(二)_第13张图片

1.首先有一个正式员工和临时员工,都从阻塞队列中读取任务。

代码的结构

正式员工 

javaEE多线程(三)---线程安全(二)_第14张图片

javaEE多线程(三)---线程安全(二)_第15张图片

javaEE多线程(三)---线程安全(二)_第16张图片

javaEE多线程(三)---线程安全(二)_第17张图片

 

你可能感兴趣的:(javaEE,java,开发语言,java-ee,面试)