Java多线程安全问题---------懒汉式线程安全、死锁

一. 懒汉式线程安全

1.饿汉式和懒汉式

在学习单例模式时,学习了什么叫饿汉式和懒汉式。

  1. 单例模式

一种设计思想,就是规定类只能创建一个对象。饿汉式和懒汉式是单例模式实现的两种方法。

  1. 饿汉式

当类加载的时候,就将对象创建好,然后在方法中返回。

public class Hungry {
	// 首先私有构造方法,确保在该类之外无法创建该类的实例
	private Hungry() {};
	// 创建该类的实例
	private static final Hungry hungry = new Hungry();
	// 提供给外面需要该实例的函数
	public static Hungry getInstanse() {
		return hungry;
	}
}

  1. 懒汉式

当类创建的时候,并没有直接在该类中创建对象,而是当调用方法的时候,才进行创建该类的实例。即延迟创建。

class Lazy{
	// 首先私有构造方法,确保在该类之外无法创建该类的实例
	private Lazy() {};
	// 先赋值为null
	private static Lazy l = null;
	// 提供给外面需要该实例的函数
	public static Lazy getInstance(){
		if(l != null) {
			l = new Lazy();
		}
		return l;
	}
}
  1. 多线程安全分析
    从代码来看,饿汉式不会造成线程安全隐患;而当采用懒汉式时,就会造成安全隐患。
    在这里插入图片描述通过上图可以看出,最终返回两个Lazy对象,违背了单例模式的思想。
  2. 懒汉式多线程安全解决方法
(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;
	}
}

这样就解决了同步函数中的问题

二、死锁

多个线程等待着正在运行线程中不可能释放的锁,从而造成死锁。

  1. 一个简单的例子,先创建两个线程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");
				}
			}
		}
	}

执行结果,两个线程都阻塞,此时程序并没有结束。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Time__Lc/article/details/88935430