8/18 Java学习博客
首先认识了4种sychronized锁 的上锁方法
1.实例方法加锁
synchronized void increase(){
}
2.实例方法加锁2
public void increase(){
synchronized(this){
}
}
3.静态方法加锁
synchronized static void increase(){
}
4.静态方法加锁2
public static void increase(){
synchronized(xx.class){ //xx就是指所在类
]
}
死锁的四个成因
1.互斥使用(基本特性)
2.不可抢占(基本特性)
3.请求保持(拿到锁1后再请求锁2,锁1不会释放)
4.循环等待
然后解决死锁的核心就在后两个
3.请求保持 -----通过尽可能避免嵌套锁这种数据结构
4.循环等待-----就是约定加锁顺序
volatile这个关键字
它带来的是可见性和有序性
可见性 它也就是内存可见性。
举个例子 我们先创建一个线程 t1 设置一个标志符 isQuit=0
while(isQuit==0){
//这个循环体 什么都不做,意味着1s内会执行很多次
}
t1.start()
然后再创建个线程t2
他就负责输入一个数来改变isQuit 使他不为0
运行之后呢 就发现 这个t1线程并没有停止,即使这个isQuit他不为0了
此处的问题就是 内存可见性引起的 (感知不到内存的变化)
是编译器错误优化导致的,由于这个循环速度飞快,短时间内就进行了大量的循环,也就是进行了大量的load和mcp操作,那这个load是很消耗资源的,此时编译器就发现了这个load操作 结果都一样 那么就进行了一个优化,就一直读取cpu寄存器里面的isQuit值了。
这就导致了线程停不下了
所以我们加了个volatile 关键字 让他每次都必须从内存里读取这个值
正规一点其实就是 volatile 保证一个线程对变量的修改,对其他线程立即可见。读取 volatile 变量时,会读取到其他线程最近写入的值,而不是一直使用自己线程可能缓存的旧值。
还有就是又复习了下join
//谁调用join 谁就等待join前的线程
// t2里面 t1.join t2就等t1
wait 和 notify
注意:!!!必须在当前线程已经持有对应对象锁的情况下调用。
wait在执行的时候有3个步骤
1.释放当前锁
2.让线程进入阻塞
3.当线程被唤醒时候重新获取到锁
notify就是通知这个线程可以准备来获取锁了 直到这个用notify的线程结束。
来个例子
当前线程获得 lock1
↓
notify t1
↓
t1:我醒了!
↓
但是 t1 还拿不到 lock1
↓
因为当前线程还在 synchronized 里面
↓
当前线程 sleep 5秒
↓
退出 synchronized
↓
释放 lock1
↓
t1 重新竞争 lock1
↓
拿到锁
↓
wait() 返回
↓
继续往下执行
不过有一点需要注意 就是 如果有多个线程都对一个锁对象进行wait 后面用notify来唤醒 是随机唤醒而不能指定唤醒
设计模式之一 单例模式
那么这个单例模式 其实就是要求 一个类只有一个实例对象
那么就通过一种设计规范来实现这个
那我们知道静态方法只会在类初始化时执行一次
所以 我们就把new这个方法写出静态方法
通过定义一个get方法来return获取实例
这里就分为了两种
1.饿汉模式
类直接创建这个实例对象
class Singleton{
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
//再把构造方法设置为私有,避免其他代码能够创建出实例
private Singleton(){}
}
}
2.懒汉模式
//也可以保证只有唯一实例
//首次调用getInstance的时候才会真正去创建实例
class SingletonLazy{
private static SingletonLazy instance;
//加锁 保证同一时间只有一个线程进入 getInstance()。 保证线程安全
public static synchronized SingletonLazy getInstance(){
if(instance==null){
instance=new SingletonLazy();
}
return instance;
}
单纯的懒汉模式会有线程不安全,在最开始对象没new出来的时候,如果有两个线程都进行了一次调用getInstance 此时就创建了两个实例对象 违反了单例模式
所以我们加锁
虽然后续每次调用这个getInstance都会加锁,但是 后续都是读操作 也就不会线程不安全了。
不过后续安全了 还要加锁 这种开销大的操作,因为加锁涉及了锁冲突 阻塞等待。
其实在单例模式学习的时候我就·想到了JavaBean
不过他们还是有比较大的区别的 ,一个就是JavaBean是一种写法规范
private 属性
+
无参构造
+
getter / setter
他解决的是 怎么规范的封装数据
而单例模式解决的是 整个程序里,这个类只创建一个对象。 就像上面这个getInstance(); 不管调用多少次 最后都是一个对象
其实跟这个单例模式有大关联的反而是SpringBean
假设
@Service
public class UserService {
}
Spring 启动的时候会创建一个 UserService 对象,放进 IOC 容器。
默认情况下,Spring Bean 的作用域是:Singleton 单例
也就是
@Autowired
UserService userService;
所以不同地方注入的 userService,默认拿到的是同一个 Bean 对象。
只要都是从同一个 Spring IOC 容器拿的,并且这个 Bean 是默认的 singleton,拿到的通常就是同一个对象。
“不同地方”不是说代码的不同行,而是说:
不同的 Spring Bean 里,都依赖并注入同一个 UserService。
一般来说,Spring 默认的 singleton Bean 生命周期,基本就是跟 IOC 容器一样长。
总结下就是
JavaBean
↓
一种普通 Java 类的编写规范
Spring Bean
↓
交给 Spring IOC 容器管理的对象
Singleton 单例模式
↓
保证一个类只有一个实例

浙公网安备 33010602011771号