延迟加载/“懒汉模式”

编程

public class MyObject {

private static MyObject myObject;

public MyObject() {

}

public static MyObject getInstance(){

if(myObject!=null){

}else {

myObject = new MyObject();

}

}

}

public class MyThread extends Thread{

@Override

public void run() {

System.out.println(MyObject.getInstance().hashCode());

}

}

public class Run {

public static void main(String[] args) {

MyThread myThread = new MyThread();

myThread.start();

MyThread myThread1 = new MyThread();

myThread1.start();

MyThread myThread2 = new MyThread();

myThread2.start();

}

}

此实验虽然取得一个对象的实例,但如果是在多线程的环境中,就会出现取出多个实例 的情况,与单例模式的初衷是相违背的

  1. 延迟加载/“懒汉模式”缺点

前面“延迟加载”示例中的代码完全错误。根本不能实现保持单例的状态。看一下如何在多线程环境中结合“错误的单例模式”创建出“多例”。

public class MyObject {

private static MyObject myObject;

public MyObject() {

}

public static MyObject getInstance(){

try{

if(myObject !=null){

}else {

//模拟在创建对象之前做了一些准备工作

Thread.sleep(3000);

myObject = new MyObject();

}

}catch (InterruptedException e){

e.printStackTrace();

}

return myObject;

}

}

public class MyThread extends Thread{

@Override

public void run() {

System.out.println(MyObject.getInstance().hashCode());

}

}

public class Run {

public static void main(String[] args) {

MyThread t1 = new MyThread();

MyThread t2 = new MyThread();

MyThread t3 = new MyThread();

t1.start();

t2.start();

t3.start();

}

}

出现了2种hashcode,说明创建了2个对象,并不是单例的,这就是错误的“单例模式”。

  1. 延迟加载/“懒汉模式”的解决方案

  • 声明synchronized关键字

    既然多线程可以同时进入getInstance()方法,那么只需要对getInstance()方法声明synchronized关键字即可。

public class MyObject {

private static MyObject myObject;

public MyObject() {

}

//整个方法被上锁,设置同步方法的效率太低了

//此方法加入同步synchronized关键字得到相同实例的对象,但此方法运行效率太低

//下一个线程想要取得对象,则必须等待上一个对象释放锁之后,才可以继续运行。

public static synchronized MyObject getInstance(){

try {

if (myObject != null) {

} else {

//模拟在创建对象之前做一些准备性的工作

Thread.sleep(3000);

myObject = new MyObject();

}

}catch (Exception e){

e.printStackTrace();

}

return myObject;

}

}

public class MyThread extends Thread{

@Override

public void run() {

System.out.println(MyObject.getInstance().hashCode());

}

}

public class Run {

public static void main(String[] args) {

MyThread myThread0 = new MyThread();

MyThread myThread1 = new MyThread();

MyThread myThread2 = new MyThread();

myThread0.start();

myThread1.start();

myThread2.start();

}

}

此方法加入同步synchronized关键字得到相同实例的对象,但此种方法的运行效率非常低下,是同步运行的,下一个线程想要取得对象,则必须等上一个线程释放锁之后,才可以继续执行。

  • 尝试同步代码块

public class MyObject {

private static MyObject myObject;

public MyObject() {

}

public static MyObject getInstance(){

synchronized (MyObject.class) {

try {

if (myObject != null) {

} else {

//模拟在创建对象之前做一些准备工作

Thread.sleep(3000);

myObject = new MyObject();

}

} catch (Exception e) {

e.printStackTrace();

}

return myObject;

}

}

}

public class MyThread extends Thread{

@Override

public void run() {

System.out.println(MyObject.getInstance().hashCode());

}

}

public class Run {

public static void main(String[] args) {

MyThread myThread1 = new MyThread();

MyThread myThread2 = new MyThread();

MyThread myThread3 = new MyThread();

myThread1.start();

myThread2.start();

myThread3.start();

}

}

此方法加入同步synchronized语句块得到相同实例的对象,但此种方法的运行效率也是非常低的,和synchronized同步方法一样同步运行。

  • 针对某些重要的代码进行单独的同步

public class MyObject {

private static MyObject myObject;

public MyObject() {

}

public static MyObject getInstance(){

try {

if (myObject != null) {

} else {

//模拟在创建对象之前做一些准备工作

Thread.sleep(3000);

//使用synchronized(MyObject.class)

//虽然部分代码被上锁

//但还是有非线程安全问题

synchronized (MyObject.class) {

myObject = new MyObject();

}

}

}catch (Exception e){

e.printStackTrace();

}

return myObject;

}

}

public class MyThread extends Thread{

@Override

public void run() {

System.out.println(MyObject.getInstance().hashCode());

}

}

public class Run {

public static void main(String[] args) {

MyThread myThread0 = new MyThread();

MyThread myThread1 = new MyThread();

MyThread myThread3 = new MyThread();

myThread0.start();

myThread1.start();

myThread3.start();

}

}

此方法使同步synchronized语句块,只对实例化对象的关键代码进行同步,从语句的结构上来讲,

运行的效率的确得到提升。但如果是遇到多线程的情况下还是无法解决得到同一个实例对象的结果。

  • 使用DCL双检查锁机制

public class MyObject {

private volatile static MyObject myObject;

public MyObject() {

}

//使用双重检测机制来解决问题,既保证了不需要同步代码的异步执行性

//又保证了单例的效果

public static MyObject getInstance(){

if(myObject!=null){

}else {

synchronized (MyObject.class){

if(myObject==null){

myObject = new MyObject();

}

}

}

return myObject;

}

/**

* 此版本的代码称为Double-Check Locking

*/

}

public class MyThread extends Thread{

@Override

public void run() {

System.out.println(MyObject.getInstance().hashCode());

}

}

public class Run {

public static void main(String[] args) {

MyThread myThread = new MyThread();

MyThread myThread1 = new MyThread();

MyThread myThread2 = new MyThread();

myThread.start();

myThread1.start();

myThread2.start();

}

}

使用双重检查锁功能,成功的解决了“懒汉模式”遇到多线程的问题。DCL也是大多数多线程结合单例模式使用的解决方案。

以上是 延迟加载/“懒汉模式” 的全部内容, 来源链接: utcz.com/z/513419.html

回到顶部