立即加载/饿汉模式

立即加载就是使用类的时候已经将对象创建完毕。

public class MyObject {
//立即加载方式==饿汉模式
private static MyObject myObject = new MyObject();
private MyObject(){
}
public static MyObject getInstance(){
//立即加载
//缺点是不能有其他实例变量
//getInstance()方法没有同步,有可能出现非线程安全问题
return myObject;
}
} public class MyThread extends Thread {
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
} public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}

运行程序,控制台打印结果如下:

1795478472
1795478472
1795478472

控制台打印的hashCode是同一个值,说明对象是同一个,也就实现了立即加载型单例模式。


延迟加载/懒汉模式

延迟加载就是在调用get()方法时实例才被创建

public class MyObject {
private static MyObject myObject;
private MyObject(){
}
public static MyObject getInstance() {
//延迟加载
if (myObject != null) {
return myObject;
}
myObject = new MyObject();
return myObject;
}
} public class MyThread extends Thread {
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
} public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}

程序运行结果如下:

1396452035

此实验虽然取得一个对象的实例,但是如果是在多线程环境中,就会出现取出多个实例的情况。对以上代码中的main函数做如下修改:

public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}

重新运行程序,控制台打印结果如下:

166471260
1795478472
1858758426

控制台打印了三个不同的hashCode,说明并没有实现单例模式。


延迟加载/懒汉模式 解决方案
public class MyObject {
private static MyObject myObject;
private MyObject(){
}
//整个方法上锁,效率较低
synchronized public static MyObject getInstance() {
//延迟加载
if (myObject != null) {
return myObject;
}
try {
//模拟一些耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myObject = new MyObject();
return myObject;
}
}

重新运行程序,控制台将打印出三个一样的hashCode。

以上方法对整个方法加锁,效率比较低。对以上代码做如下修改:

public class MyObject {
private static MyObject myObject;
private MyObject(){
}
public static MyObject getInstance() {
//延迟加载
if (myObject != null) {
return myObject;
}
try {
//模拟一个耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (MyObject.class) {
myObject = new MyObject();
}
return myObject;
}
}

重新运行程序,控制台打印结果如下:

774088025
641502649
1367113803

以上代码虽然解决的效率问题,但是仍然没有保证只创建一个实例。继续对以上代码做如下修改:

public class MyObject {
private static MyObject myObject;
private MyObject(){
}
public static MyObject getInstance() {
//延迟加载
if (myObject != null) {
return myObject;
}
try {
//模拟一个耗时操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (MyObject.class) {
if (myObject == null) {
myObject = new MyObject();
}
}
return myObject;
}
}

重新运行程序,控制台输入结果如下:

641502649
641502649
641502649

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


使用静态内置类实现单例模式
public class MyObject {
private static class MyObjectHandler{
private static MyObject myObject = new MyObject();
}
private MyObject(){ }
public static MyObject getInstance () {
return MyObjectHandler.myObject;
}
}

运行程序,控制台输出结果如下:

774088025
774088025
774088025

序列化与反序列化的单例模式实现
public class MyObject implements Serializable {
private static final long serialVersionUID = 1L;
private static class MyObjectHandler{
private static MyObject myObject = new MyObject();
}
private MyObject(){ }
public static MyObject getInstance () {
return MyObjectHandler.myObject;
}
} public class SaveAndRead {
public static void main(String[] args) {
try {//序列化对象到磁盘
MyObject myObject = MyObject.getInstance();
FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/shangyidong/Downloads/myObjectFile.txt"));
ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutputStream);
objectOutput.writeObject(myObject);
objectOutput.close();
fileOutputStream.close();
System.out.println(myObject.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
/****************/
try {//从磁盘反序列化到对象
FileInputStream fileInputStream = new FileInputStream(new File("/Users/shangyidong/Downloads/myObjectFile.txt"));
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
MyObject myObject = (MyObject) objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
System.out.println(myObject.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}

运行程序,控制台打印结果如下:

988487739
1878277481

控制台打印出的hashCode不同,存储到磁盘上的对象和从磁盘读取出来的对象并不是同一个对象。对以上代码做如下修改:

public class MyObject implements Serializable {
private static final long serialVersionUID = 1L;
private static class MyObjectHandler{
private static MyObject myObject = new MyObject();
}
private MyObject(){ }
public static MyObject getInstance () {
return MyObjectHandler.myObject;
}
protected Object readResolve(){
System.out.println("readResolve invoked");
return MyObjectHandler.myObject;
}
}

重新运行程序,控制台打印结果如下:

1066557918
readResolve invoked
1066557918

反序列化时使用了ReadResolve()方法,存储到磁盘上的对象和从磁盘上取出来的对象是同一个对象。


使用static代码块实现单例模式

静态代码块中的代码在使用类的时候就已经执行了,所以可以应用静态代码块的这个特性来实现单例模式。

public class MyObject {
private static MyObject myObject = null;
private MyObject(){ }
static{
myObject = new MyObject();
}
public static MyObject getInstance() {
return myObject;
}
} public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(MyObject.getInstance().hashCode());
}
}
} public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}

运行程序,控制台输出结果如下:

708252873
708252873
708252873
708252873
708252873
708252873
708252873
708252873
708252873

使用enum枚举数据类型实现单例模式

枚举enum和静态代码块的特性相似,在使用枚举类时,构造方法会被自动调用,也可以实现单例设计模式。

public enum MyObject {
connectionFactory;
private Connection connection;
private MyObject(){
try {
System.out.println("创建MyObject对象");
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user = "root";
String password = "";
String driver = "com.mysql.jdbc.Driver";
Class.forName(driver);
connection = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
}
}
public Connection getConnection() {
return connection;
}
} public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(MyObject.connectionFactory.getConnection().hashCode());
}
}
} public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}

运行程序,控制台打印结果如下:

创建MyObject对象
1253195928
1253195928
1253195928
1253195928
1253195928
1253195928
1253195928
1253195928
1253195928

完善使用enum枚举实现单例模式

上面的代码中将枚举类进行了暴露,违反了“职责单一原则”。下面进行改善:

public class MyObject {
public enum MyEnumSingleton{
connectionFactory;
private Connection connection;
private MyEnumSingleton(){
try {
System.out.println("创建MyObject对象");
String url = "jdbc:mysql://127.0.0.1:3306/test";
String user = "root";
String password = "";
String driver = "com.mysql.jdbc.Driver";
Class.forName(driver);
connection = DriverManager.getConnection(url, user, password);
} catch (Exception e) {
e.printStackTrace();
}
}
public Connection getConnection() {
return connection;
}
}
public static Connection getConnection() {
return MyEnumSingleton.connectionFactory.getConnection();
}
} public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(MyObject.getConnection().hashCode());
}
}
} public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}

程序运行结果如下:

创建MyObject对象
2039988480
2039988480
2039988480
2039988480
2039988480
2039988480
2039988480
2039988480
2039988480

Java多线程编程核心技术---单例模式与多线程的更多相关文章

  1. Java多线程编程核心技术

    Java多线程编程核心技术 这本书有利于对Java多线程API的理解,但不容易从中总结规律. JDK文档 1. Thread类 部分源码: public class Thread implements ...

  2. 《Java 多线程编程核心技术》- 笔记

    作为业务开发人员,能够在工作中用到的技术其实不多.虽然平时老是说什么,多线程,并发,注入,攻击!但是在实际工作中,这些东西不见得用得上.因为,我们用的框架已经把这些事做掉了. 比如web开发,外面有大 ...

  3. Java多线程编程核心技术(三)多线程通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  4. Java多线程编程核心技术(二)对象及变量的并发访问

    本文主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.阅读本文应该着重掌握如下技术点: synchronized对象监视器为O ...

  5. Java多线程编程核心技术(一)Java多线程技能

    1.进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程. 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位. 举个例子: 打开你的计算机上的任务管 ...

  6. 《Java多线程编程核心技术》知识梳理

    <Java多线程编程核心技术> @author ergwang https://www.cnblogs.com/ergwang/ 文章末尾附pdf和png下载链接 第1章 Java多线程技 ...

  7. Java多线程编程核心技术---学习分享

    继承Thread类实现多线程 public class MyThread extends Thread { @Override public void run() { super.run(); Sys ...

  8. Java多线程编程核心技术---对象及变量的并发访问(二)

    数据类型String的常量池特性 在JVM中具有String常量池缓存的功能. public class Service { public static void print(String str){ ...

  9. 《Java多线程编程核心技术》推荐

    写这篇博客主要是给猿友们推荐一本书<Java多线程编程核心技术>. 之所以要推荐它,主要因为这本书写得十分通俗易懂,以实例贯穿整本书,使得原本抽象的概念,理解起来不再抽象. 只要你有一点点 ...

随机推荐

  1. struts2 action 页面跳转

    struts2 action 页面跳转 标签: actionstruts2redirect 2013-11-06 16:22 20148人阅读 评论(0) 收藏 举报 (1)type="di ...

  2. 关于使用 pushViewController: animated: 方法在下一个控制器中拿不到值的解决方法

    如下代码: // 加载一个tabbar控制器 houseMessageTabbarController *houseTabbar = [[houseMessageTabbarController al ...

  3. java integer对象判断两个数字是否相等

    java integer对象判断两个数字是否相等,不一定对 问题发生的背景:javaweb的项目,起先,因为在java中实体类中的int类型在对象初始化之后会给int类型的数据默认赋值为0,这样在很多 ...

  4. webbench详解

    安装 mkdir -p /usr/local/man/man1 yum install ctags -y tar zxvf webbench-1.5.tar.gzcd webbench-1.5make ...

  5. PHP预定义接口

    目录 引言 IteratorAggregate(聚合式aggregate迭代器Iterator) Countable ArrayAccess Iterator 总结 引言 在PHP中有好几个预定义的接 ...

  6. SQLite遇到的关于x64、x86问题

    初次使用SQLite遇到了莫名其妙的问题: 未能加载文件或程序集“System.Data.SQLite, Version=1.0.92.0, Culture=neutral, PublicKeyTok ...

  7. 在CentOS上安装Sublime Text

    CentOS 是基于 Red Hat (RHEL) 的, 其中并没有包管理工具 apt. 最近需要在装了 CentOS 系统的服务器上安装Sublime Text, 到官网上看了一下, 对其他 (De ...

  8. Android Studio目录结构浅析

    让我们来简单了解下Android Studio中不同目录(文件)的位置和用途.首先看下一个App的最简单的目录结构 OK,我们这么看,第一,把这么多文件先分成这么三块1. 编译系统(Gradle)2. ...

  9. FZU 1752 A^B mod C(快速加、快速幂)

    题目链接: 传送门 A^B mod C Time Limit: 1000MS     Memory Limit: 65536K 思路 快速加和快速幂同时运用,在快速加的时候由于取模耗费不少时间TLE了 ...

  10. Linux tcpdump 命令详解

    简介用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的& ...