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 单例模式

保证一个类只有一个实例

posted @ 2026-08-30 12:32  曾章吉的浩吧  阅读(4)  评论(0)    收藏  举报