本文已参与「新人创作礼」活动,一起开启掘金创作之路。
死锁
让我们先来看看死锁的代码是怎么样的。
synchronized public void add(String a){
synchronized(this){
System.out.println(a);
}
}
这个代码怎么理解呢?
第一步,进入外层锁,这个是正常的。
第二步,要进入内层锁,这个是不行的,为什么呢?
因为这个是同一把锁,里面要能锁,必须是外面释放锁才可以,可是外面释放锁必须是里面的内容执行完,才可以释放。
所以这样就造成了死锁
JVM对此就研发了一种叫可重复锁的锁,来解决这种两个锁锁一个的问题。synchronize就是这种可重复锁。
当然这也会带来效率下降。
死锁的四个条件
1.互斥使用: 一个锁被一个线程占用之后,其他线程占用不了
2.不可占用: 一个锁被一个线程占用之后,其他线程不能把这个锁抢走
3.请求和保持: 当一个线程占用多把锁之后,除非显示的释放,否则这些锁始终都是被该线程占用着。
4.环路等待: 等待关系连成了一个环
volatile
是禁止编译器优化,保证内存可见性。并不保证事务的原子性
JMM
JMM也叫java内存模型分为工作内存(CPU 寄存器)和主内存(内存)。
wait 和 notify
wait 和 notify 都是Object对象的方法
调用wait方法的线程,就会陷入阻塞
阻塞到其他线程通过notify来通知
来看代码
public class Demo15 {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
System.out.println("before wait");
object.wait();
System.out.println("after wait");
}
}
这个会报非法的锁状态错误这个是为什么呢?
原来wait内部会做三件事情
1.先释放锁
2.等待通知
3.收到通知之后,重新获取锁,并继续执行下去
所以wait要搭配synchronized
public class Demo15 {
public static void main(String[] args) throws InterruptedException {
Object object = new Object();
synchronized(object){ //wait哪个对象。就要针对哪个对象加锁
System.out.println("before wait");
object.wait();
System.out.println("after wait");
}
}
}
我们可以看到。没有notify代码就会一直执行着,那接下来我们看看他们两个联手会怎么样。
public class Demo16 {
public static Object object = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(()->{
synchronized (object){
try {
object.wait();
System.out.println("wait");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
Thread t2 = new Thread(()->{
synchronized (object){
object.notify();
System.out.println("notify");
}
});
t2.start();
}
}
但是当我们有很多的wait,这个时候notify,唤醒的是哪个wait是不知道的。
因为线程的争抢式执行,导致最后唤醒的是哪个线程就不知道了
notifyAll是一下子把所有wait唤醒
单例模式
要求代码中的某个类只能有一个实例,不能有多个
例如 JDBC DataSource,这样的对象,就应该是单例的
懒汉模式
//懒汉模式
//这个实现是比较重要的,要牢记。
class Singleton2{
//这个就是和饿汉模式的不同,只有用到的时候才会初始化。
// volatile 是为了内存可见性
private volatile static Singleton2 instance = null;
private Singleton2(){}
public static Singleton2 getInstance(){
//这个if是为了提高效率,如果没有这个的话,那么每一次来都会创建一个锁
if(instance == null){
//创建锁是因为这边的事务不是原子性的,多线程的时候保证安全。
synchronized (Singleton2.class){
if(instance == null){ //这个就是单纯的看看是不是null
instance = new Singleton2();
}
}
}
return instance;
}
}
public class Demo18 {
public static void main(String[] args) {
Singleton2 singleton2 = Singleton2.getInstance();
}
}
两个if的作用是不一样的。
饿汉模式
//饿汉模式
class Singleton{
//使用static创建一个实例,并且立马对其实例化
private static Singleton instance = new Singleton();
//让构造方法private是为了不让别人构造,这样我们就有了唯一实例。
private Singleton(){}
//提供方法,获得这个唯一的实例
public static Singleton getInstance(){
return instance;
}
}
public class Demo17 {
public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
}
}
为什么叫它饿汉呢?是因为创建实例比较着急,直接创建。不管你要不要,我都先创建一个。