【码农吧干货】单例模式
/ 单例模式 /
官方解释:Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)
我的理解:单例模式是实现类型最多的设计模式之一,本文给出两种实现模式,饿汉式、懒汉式的单例模式。
参与者:Singleton单例类
类图:
/ 单例模式——饿汉式 /
饿汉式实现步骤
第一步,私有化构造函数;
第二步,定义时初始化;
第三步,定义getInstance()方法返回对象实例;
第四步,新建线程类验证线程安全性(此步骤为验证该种单例实现方式线程安全性,可选)
饿汉式实现代码
/**
* Created by 30292 on 2020/6/25.
*/
public class SingleInstance {
public static void main(String[] args) {
MyThread _tMyThread = new MyThread();
MyThread _tMyThread2 = new MyThread();
MyThread _tMyThread3 = new MyThread();
_tMyThread.start();
_tMyThread2.start();
_tMyThread3.start();
}
}
class SingleObject {
private SingleObject() { //第一步 私有化构造函数 禁止客户端用new新建对象
}
//第二步 提供新建对象入口 这里采用饿汉式
private static SingleObject _sSingleObject = new SingleObject();//饿汉式 定义时初始化 线程安全
// static 方法 保证客户端可以使用类名调用(即保证客户端在未新建对象时 可以调用此方法)
public static SingleObject getInstance() {
return _sSingleObject;
}
}
class MyThread extends Thread { //线程类用来测试 线程安全性
public void run() {
System.out.println(SingleObject.getInstance().hashCode());
}
}
输出:
389269157
389269157
389269157
/ 单例模式——懒汉式 /
懒汉式实现步骤
第一步,私有化构造函数;
第二步,新建单例引用,并在构造函数中初始化;
第三步,定义getInstance()方法返回对象实例;
第四步,新建线程类验证线程安全性(此步骤为验证该种单例实现方式线程安全性,可选)。
懒汉式——线程不安全
public class SingleInstance {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) { // 模拟10个线程
new MyThread().start();
}
}
}
class SingleObject {
private SingleObject() { //第一步 私有化构造函数 禁止客户端用new新建对象
}
private static SingleObject _sSingleObject;
// static 方法 保证客户端可以使用类名调用(即保证客户端在未新建对象时 可以调用此方法)
public static SingleObject getInstance() throws Exception {
Thread.sleep(5000); //通过sleep() 等待5s 让线程不安全的问题暴露出来
if (_sSingleObject == null) {
_sSingleObject = new SingleObject();
}
return _sSingleObject;
}
}
class MyThread extends Thread { //线程类用来测试 线程安全性
public void run() {
try {
System.out.println(SingleObject.getInstance().hashCode());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出:
193202248
412303685
193202248
193202248
193202248
389269157
193202248
1435984462
193202248
193202248
懒汉式 synchronize同步方法保证线程安全
public class SingleInstance {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) { // 模拟10个线程
new MyThread().start();
}
}
}
class SingleObject {
private SingleObject() { //第一步 私有化构造函数 禁止客户端用new新建对象
}
private static SingleObject _sSingleObject;
// static 方法 保证客户端可以使用类名调用(即保证客户端在未新建对象时 可以调用此方法)
public static synchronized SingleObject getInstance() throws Exception {
Thread.sleep(5000); //通过sleep() 等待5s 让线程不安全的问题暴露出来
if (_sSingleObject == null) {
_sSingleObject = new SingleObject();
}
return _sSingleObject;
}
}
class MyThread extends Thread { //线程类用来测试 线程安全性
public void run() {
try {
System.out.println(SingleObject.getInstance().hashCode());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出:
968687297
968687297
968687297
968687297
968687297
968687297
968687297
968687297
968687297
968687297
懒汉式 synchronized同步代码块保证线程安全
public class SingleInstance {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) { // 模拟10个线程
new MyThread().start();
}
}
}
class SingleObject {
private SingleObject() { //第一步 私有化构造函数 禁止客户端用new新建对象
}
private static SingleObject _sSingleObject;
// static 方法 保证客户端可以使用类名调用(即保证客户端在未新建对象时 可以调用此方法)
public static SingleObject getInstance() throws Exception {
Thread.sleep(5000); //通过sleep() 等待5s 让线程不安全的问题暴露出来
synchronized (SingleObject.class) { //将需要原子化操作的代码放在同步代码块中,static方法的同步锁为类字节码,非static方法的同步锁为this,此处static方法
//同步代码块相对于同步方法 同步的内容少,效率高
if (_sSingleObject == null) {
_sSingleObject = new SingleObject();
}
}
return _sSingleObject;
}
}
class MyThread extends Thread { //线程类用来测试 线程安全性
public void run() {
try {
System.out.println(SingleObject.getInstance().hashCode());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出:
774856018
774856018
774856018
774856018
774856018
774856018
774856018
774856018
774856018
774856018
懒汉式 lock机制保证线程安全
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SingleInstance {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) { // 模拟10个线程
new MyThread().start();
}
}
}
class SingleObject {
private SingleObject() { //第一步 私有化构造函数 禁止客户端用new新建对象
}
private static Lock lock = new ReentrantLock();
private static SingleObject _sSingleObject;
// static 方法 保证客户端可以使用类名调用(即保证客户端在未新建对象时 可以调用此方法)
public static SingleObject getInstance() throws Exception {
Thread.sleep(5000); //通过sleep() 等待5s 让线程不安全的问题暴露出来
lock.lock();
try {
if (_sSingleObject == null) {
_sSingleObject = new SingleObject();
}
} finally {
lock.unlock();
}
return _sSingleObject;
}
}
class MyThread extends Thread { //线程类用来测试 线程安全性
public void run() {
try {
System.out.println(SingleObject.getInstance().hashCode());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出:
774856018
774856018
774856018
774856018
774856018
774856018
774856018
774856018
774856018
774856018
/ 小结 /
本文介绍单例模式及其两种实现方式——饿汉式和懒汉式,这是单例模式的最常见的两种实现方式,其实单例模式的实现方式不只这两种,还包括静态代码块和枚举方式实现的单例模式,下一篇文章中再讨论。
end
转发是最好的赞赏!!!