单例模式与多线程

立即加载/饿汉模式

立即加载就是使用类的时候已经将对象创建完毕,常见的实现办法就是直接new实例化。

立即加载/饿汉模式实在调用方法前,实例已经被创建了

package Six;

public class MyObject {

    // 立即加载方式==饿汉模式
private static MyObject myObject = new MyObject(); private MyObject() {
} public static MyObject getInstance() {
// 此代码版本为立即加载
// 此版本代码的缺点是不能有其它实例变量
// 因为getInstance()方法没有同步
// 所以有可能出现非线程安全问题
return myObject;
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
} }
package Six;

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(); } }

说明对象是同一个,也就实现了立即加载型单例设计模式

延迟加载/"懒汉模式"

延迟加载就是在调用get()方法时实例才被创建,常见的实现办法就是在get()方法中进行new实例化

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
// 延迟加载
if (myObject != null) {
} else {
myObject = new MyObject();
}
return myObject;
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
}
package Six;

public class Run {

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

此实验虽然取得一个对象的实例,但如果实在多线程的环境中,就会出现多个实例的情况

延迟加载/"懒汉模式"缺点

延迟加载在多线程环境中是错误的。

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
} }
package Six;

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(); } }

打印出了三个对象,并不是单例的,如何解决?见下

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

(1)声明synchronized关键字

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

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} synchronized public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

但此方法的运行效率底下,是同步运行的,下一个线程想要取得对象,则必须等上一个线程释放锁后,才可以继续执行。

(2)尝试同步代码块

同步方法是对方法的整体进行持锁,这对运行效率来讲是不利的。等同于上面的写法

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
synchronized (MyObject.class) {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
myObject = new MyObject();
}
} } catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

(3)针对某些重要的代码进行单独的同步

同步代码块可以针对某些重要的代码进行单独的同步,而其他的代码则不需要同步,这样在运行时,效率完全可以得到大幅度提升

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
synchronized (MyObject.class) {
myObject = new MyObject();
} }
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

此方法只对实例化对象的关键代码进行同步,从语句的结构上讲,运行效率得到了提升,但在多线程的环境下还是无法解决得到一个实例对象的结果。

(4)使用DCl双检查锁机制

package Six;

public class MyObject {

    private static MyObject myObject;

    private MyObject() {
} public static MyObject getInstance() {
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(3000);
synchronized (MyObject.class) {
if(myObject ==null) {
myObject = new MyObject();
} } }
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

DCL是大多数多线程结合单例模式使用的解决方案

使用静态内置类实现单例模式

其他代码同上

package Six;

public class MyObject {

    // 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
} private MyObject() {
} public static MyObject getInstance() {
return MyObjectHandler.myObject;
} }

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

package Six;

public class MyObject {

    private static MyObject instance = null;

    private MyObject() {
} static {
instance = new MyObject();
} public static MyObject getInstance() {
return instance;
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.getInstance().hashCode());
}
}
}
package Six;

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(); } }

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

package Six;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; public enum MyObject {
connectionFactory; private Connection connection; private MyObject() {
try {
System.out.println("调用了MyObject的构造");
String url = "jdbc:sqlserver://localhost:1079;databaseName=ghydb";
String username = "sa";
String password = "";
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(driverName);
connection = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} public Connection getConnection() {
return connection;
}
}
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.connectionFactory.getConnection()
.hashCode());
}
}
}
package Six;

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(); } }

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

前面违反了"职责单一原则”

package Six;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; public class MyObject { public enum MyEnumSingleton {
connectionFactory; private Connection connection; private MyEnumSingleton() {
try {
System.out.println("创建MyObject对象");
String url = "jdbc:sqlserver://localhost:1079;databaseName=y2";
String username = "sa";
String password = "";
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(driverName);
connection = DriverManager.getConnection(url, username,
password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
} public Connection getConnection() {
return connection;
}
} public static Connection getConnection() {
return MyEnumSingleton.connectionFactory.getConnection();
} }
package Six;

public class MyThread extends Thread {

    @Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(MyObject.getConnection().hashCode());
}
}
}
package Six;

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(); }
}

《Java多线程编程核心技术》读后感(十四)的更多相关文章

  1. Java多线程编程核心技术,第四章

    1,ReentrantLock 2,object的wait(),wait(x),notify(),notifyAll(),分别等于Condition的await(),await(x,y),signal ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. 《java多线程编程核心技术》(一)使用多线程

    了解多线程 进程和多线程的概念和线程的优点: 提及多线程技术,不得不提及"进程"这个概念.百度百科对"进程"的解释如下: 进程(Process)是计算机中的程序 ...

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

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

随机推荐

  1. Linux系统调用及用户编程接口(API)

    系统调用 所谓系统调用是指操作系统提供给用户程序调用的一组"特殊"接口,用户程序能够通过这组"特殊"接口来获得操作系统内核提供的服务.比如用户能够通过进程控制相 ...

  2. 【BZOJ2843】极地旅行社 离线+树链剖分+树状数组

    [BZOJ2843]极地旅行社 Description 不久之前,Mirko建立了一个旅行社,名叫“极地之梦”.这家旅行社在北极附近购买了N座冰岛,并且提供观光服务.当地最受欢迎的当然是帝企鹅了,这些 ...

  3. LRM-00109: could not open parameter file

    SQL>startup                                                                                       ...

  4. 使用struts2中默认的拦截器以及自定义拦截器

    转自:http://blog.sina.com.cn/s/blog_82f01d350101echs.html 如何使用struts2拦截器,或者自定义拦截器.特别注意,在使用拦截器的时候,在Acti ...

  5. pycharm注册码地址

    (1)地址:http://idea.lanyus.com/ (2)注意,在破解的时候,是先修改hosts文件所在路径:“C:\Windows\System32\drivers\etc\hosts”,修 ...

  6. git merge的本质

    1 git merge [branch] 将[branch]这个分支merge到当前分支. 2 merge的本质 merge就是把branch上的提交合入当前分支的提交树,这两个分支上的所有提交的历史 ...

  7. go colly proxy 代理ip 动态 ip

    package main import ( "fmt" "github.com/gocolly/colly" "github.com/gocolly/ ...

  8. 【学习笔记】快速傅里叶变换(FFT)

    [学习笔记]快速傅里叶变换 学习之前先看懂这个 浅谈范德蒙德(Vandermonde)方阵的逆矩阵的求法以及快速傅里叶变换(FFT)中IDFT的原理--gzy hhh开个玩笑. 讲一下\(FFT\) ...

  9. IOS 十六进制字符串转换成UIColor

    /** * 十六进制转换成UIColor * * @param stringToConvert 十六进制字符串 * * @return UIColor */ +(UIColor *) hexStrin ...

  10. IOS 获取 文件(UIImage) 创建时间

    Image 在手机里 如果是手机拍的那么能使用ALAssetsLibrary获取一系列 图片的信息 颜色.GPS.尺寸.创建日期等 使用ALAssetsLibrary导入框架AssetsLibrary ...