一. 懒汉式线程安全
1.饿汉式和懒汉式
在学习单例模式时,学习了什么叫饿汉式和懒汉式。
- 单例模式
一种设计思想,就是规定类只能创建一个对象。饿汉式和懒汉式是单例模式实现的两种方法。
- 饿汉式
当类加载的时候,就将对象创建好,然后在方法中返回。
public class Hungry {
// 首先私有构造方法,确保在该类之外无法创建该类的实例
private Hungry() {};
// 创建该类的实例
private static final Hungry hungry = new Hungry();
// 提供给外面需要该实例的函数
public static Hungry getInstanse() {
return hungry;
}
}
- 懒汉式
当类创建的时候,并没有直接在该类中创建对象,而是当调用方法的时候,才进行创建该类的实例。即延迟创建。
class Lazy{
// 首先私有构造方法,确保在该类之外无法创建该类的实例
private Lazy() {};
// 先赋值为null
private static Lazy l = null;
// 提供给外面需要该实例的函数
public static Lazy getInstance(){
if(l != null) {
l = new Lazy();
}
return l;
}
}
- 多线程安全分析
从代码来看,饿汉式不会造成线程安全隐患;而当采用懒汉式时,就会造成安全隐患。
通过上图可以看出,最终返回两个Lazy对象,违背了单例模式的思想。 - 懒汉式多线程安全解决方法
(1)同步函数
class Lazy{
// 首先私有构造方法,确保在该类之外无法创建该类的实例
private Lazy() {};
// 先赋值为null
private static Lazy l = null;
// 提供给外面需要该实例的函数
public synchronized static Lazy getInstance(){
if(l != null) {
l = new Lazy();
}
return l;
}
}
缺点:这样每次线程执行时,就会判断下这段代码能否执行,降低效率。
(2) 同步代码块
class Lazy{
// 首先私有构造方法,确保在该类之外无法创建该类的实例
private Lazy() {};
// 先赋值为null
private static Lazy l = null;
// 提供给外面需要该实例的函数
public static Lazy getInstance(){
if(l != null) {
synchronized(Lazy.class) {
if(l != null) {
l = new Lazy();
}
}
}
return l;
}
}
这样就解决了同步函数中的问题
二、死锁
多个线程等待着正在运行线程中不可能释放的锁,从而造成死锁。
- 一个简单的例子,先创建两个线程A、B,然后创建两个对象a、b用来做锁对象。让每个线程都用synchronized锁住(A先锁a,再去锁b;B先锁b,再锁a),如果A锁住a,B锁住b,A就没办法锁住b,B也没办法锁住a,这时就陷入了死锁
public class DeadLock {
public static void main(String[] args) throws InterruptedException {
Runnable01 r01 = new Runnable01();
Runnable01 r02 = new Runnable01();
r01.flag = true;
r02.flag = false;
Thread t1 = new Thread(r01);
Thread t2 = new Thread(r02);
t1.start();
t2.start();
}
}
class Runnable01 implements Runnable{
boolean flag = true;
static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
// TODO Auto-generated method stub
// 先锁o1 然后再用o2锁
if(flag) {
synchronized (o1) {
try {
System.out.println(Thread.currentThread().getName() + "挂起");
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " true");
}
}
// 先用o2锁 再用o1锁
}else {
synchronized (o2) {
try {
System.out.println(Thread.currentThread().getName() + "挂起");
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + " false");
}
}
}
}
执行结果,两个线程都阻塞,此时程序并没有结束。