概述
目录
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;//原子的
c.代码的重排序
加上volatile SomeObject so;可以防止代码的重排序。(就不会把她重排序)
手里拿着钥匙(引用)可以去装修房子,拿着毛胚房没用
回顾:判断是否需要保证原子性的两个特点
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加锁
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的功能一模一样。
现在更推荐Lock锁相比于synchronized锁
wait、notify天生于sync绑定
5.阻塞队列------BlockingQueue(来自queue的子接口)
阻塞队列:我取不到东西的时候,我就一直等在那里
put(e) 队列满的情况下放就会阻塞,当有人让线程结束时,放入失败了,也会结束、take();
还有超时的:
6.自己实现的阻塞队列------ArrayBlockingQueue(循环队列)
一个生产者和一个消费者的问题
生产-消费者模型
生产者:一个(多个线程),只负责向队列放入元素
消费者:一个(多个线程),只负责从队列取出元素
线程和线程之间需要互相等待和通知wait()、notify();
7.定时器(类)
定时器执行任务时,不会占用我们的当前执行流。
Timer类------任务调度作用(闹钟)
abs TimerTask(本身是抽象类)-----继承这个类,重写run方法即可
模式:
多少秒之后执行什么任务。
周期性的执行任务
7.1定时器的使用
7.2自己实现一个定时器(重点)
一个延时任务,需要创建一个线程
java中的Timer实现的方式:一个任务,执行多个任务
创建一个工作线程:用优先级阻塞队列,当有很多任务让Timer去执行的时候,把任务放入优先级队列,然后创建一个死循环不断的从优先级队列中去任务执行。
问题:先执行哪个任务?
1.delay时间最小的任务。
其实定时器也是个生产者消费者线程
7.2优化版本的定时器
多次执行,执行周期性任务
如果有新线程进来,会唤醒所有任务,然后再比较看现在谁应该被执行
掌握第一个版本即可。
8.面试题:sleep和wait的区别
a.语义不同:休眠VS等待
b.sleep()一定固定休眠时间,wait(timeout)有俩个结束条件:超时时间已到or条件满足。
c.sleep是静态方法
object()普通方法
d.sleep和锁没关系,
wait是释放当前的锁。
9.线程池
9.2 为什么使用线程池?
因为线程的创建和销毁都需要消耗一定的成本。
线程池模式:
提前创建好很多线程(按需创建)
来了新任务交给储备的线程处理。
处理完了让其重新回到空闲的状态
9.3Java中的TreadPoolExecutor的构造方法+创建流程。
构造方法:
9.4线程池
也是生产者消费者模型
service.execute方法把task任务传入阻塞队列里面
TreadPoolExecutor(按照按需创建的过程)
1.一开始,线程池里一个工作线程也没有
2.随着任务的提交
//当队列已满时,才会创建新线程
构造方法参数:
Executor(接口)
Executors(定义了一些固定策略的线程池)
问题
9.5 自己实现一个线程池,我写的是线程不安全的(这里不太懂)
1.首先有一个正式员工和临时员工,都从阻塞队列中读取任务。
代码的结构
正式员工
最后
以上就是繁荣面包为你收集整理的javaEE多线程(三)---线程安全(二)的全部内容,希望文章能够帮你解决javaEE多线程(三)---线程安全(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复