搜尋
首頁Javajava教程Java單利模式與多執行緒總結歸納

概念:

  java中單例模式是一種常見的設計模式,單例模式分三種:懶漢式單例、餓漢式單例、登記式單例三種。

  單例模式有一下特點:

  1、單例類別只能有一個實例。

  2、單例類別必須自行建立自己的唯一實例。

  3、單例類別必須提供給所有其他物件此實例。

  單例模式確保某個類別只有一個實例,而且自行實例化並向整個系統提供這個實例。在電腦系統中,執行緒池、快取、日誌物件、對話方塊、印表機、顯示卡的驅動程式物件常被設計成單例。這些應用都或多或少具有資源管理器的功能。每台電腦可以有若干台印表機,但只能有一個Printer Spooler,以避免兩個列印作業同時輸出到印表機。每台電腦可以有若干通信端口,系統應集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。總之,選擇單例模式就是為了避免不一致狀態,避免政出多頭。

這裡主要詳細介紹兩種:懶漢式和餓漢式

一、立即載入/餓漢式

在呼叫方法前,實例就已經被創建,程式碼:

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

package com.weishiyao.learn.day.singleton.ep;
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
}

   

建立運行類別

package com.weishiyao.learn.day.singleton.ep;
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
t.start();
t.start();
t.start();
}
}

   

運作結果


hashCode是同一個值,表示物件也是同一個,說明實作了立即載入型的單利模式


二、延遲載入/懶漢式

在呼叫方法以後實例才會被創建,實現方案可以是將實例化放到無參構造函數當中,這樣只有當調用的時候才會創建物件的實例,程式碼:

package com.weishiyao.learn.day.singleton.ep;
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
// 延迟加载
if (myObject != null) {
} else {
myObject = new MyObject();
}
return myObject;
}
}

   


創建執行緒類

package com.weishiyao.learn.day.singleton.ep;
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(MyObject.getInstance().hashCode());
}
}

   

1 167772895

這樣雖然取出了一個物件的實例,但如果在多執行緒的環境中,就會出現多個實例的情況,這樣就不是單例模式了

運行測試類

package com.weishiyao.learn.day8.singleton.ep2;
public class Run {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}

   

運行結果

203 3 1851889404

4 188820504
5 1672864109

既然出現問題,就要解決問題,在懶漢模式中的多線程的解決方案,代碼:


第一個方案,最常見的方法,代碼:

加上不同的位置

第一種,方法鎖定

package com.weishiyao.learn.day.singleton.ep;
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
t.start();
t.start();
t.start();
t.start();
t.start();
}
}

   


這種synchronized的同步方案導致效率過於低下,整個方法都被鎖住





這種方法效率一樣很低,方法內的所有代碼都被鎖住,只需要鎖住關鍵代碼就好,第三種synchronized使用方案

package com.weishiyao.learn.day.singleton.ep;
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
synchronized public static MyObject getInstance() {
// 延迟加载
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep(); myObject = new MyObject(); }
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

   


這麼寫優方案了,但是,運行一下結果,發現,其實它是非線程安全的

結果:

1 1224717057

2 971173439
3 1851889404157

Why?

雖然鎖住了物件創建的語句,每次只能有一個線程完成創建,但是,當第一個線程進來創建完成Object物件以後,第二個線程進來還是可以繼續創建的,因為我們緊緊緊鎖定了創建語句,這個問題解決方案

package com.weishiyao.learn.day.singleton.ep;
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
// 延迟加载
try {
synchronized (MyObject.class) {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep();
myObject = new MyObject();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

   

只需要在鎖裡面再添加一個判斷,就可以保證單例了,這個是DCL雙檢查機制

結果如下:結果如下:

1 1224717057
2 1224717057

3 1224717057

4 1224717057
5 1224717057

   




線程類別代碼

package com.weishiyao.learn.day.singleton.ep;
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
// 延迟加载
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep();
synchronized (MyObject.class) {
myObject = new MyObject();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

   

運行類

package com.weishiyao.learn.day.singleton.ep;
public class MyObject {
private static MyObject myObject;
private MyObject() {
}
public static MyObject getInstance() {
// 延迟加载
try {
if (myObject != null) {
} else {
// 模拟在创建对象之前做一些准备性的工作
Thread.sleep();
synchronized (MyObject.class) {
if (myObject == null) {
myObject = new MyObject();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return myObject;
}
}

   


結果


1851889404

1851889404

18518894041851889404內部靜態類,得到了線程安全的單例模式


四、序列化和反序列化單例模式

內建靜態類別可以達到執行緒安全性的問題,但如果遇到序列化物件時,使用預設方式得到的結果還是多例的

MyObject程式碼

package com.weishiyao.learn.day.singleton.ep;
public class MyObject {
// 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
public MyObject() {
}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}

   

結果

1 970928725

2 1099149023

兩個不同的hashCode,證明並不是同一個對象,解決方案,加入下面這段程式碼

reee得到同一個物件

System.out.println(myObject.readResolve().hashCode());

結果


1 1255301379

2 調用了RereadF2 方法!
3 1255301379

相同的hashCode,證明得到了同一個物件

五、使用static程式碼區塊實作單例

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

MyObject类

package com.weishiyao.learn.day.singleton.ep;
public class MyObject {
private static MyObject instance = null;
private MyObject() {
super();
}
static {
instance = new MyObject();
}
public static MyObject getInstance() {
return instance;
}
}

   

线程类

package com.weishiyao.learn.day.singleton.ep;
public class MyThread extends Thread {
@Override
public void run() {
for (int i = ; i < ; i++) {
System.out.println(MyObject.getInstance().hashCode());
}
}
}

   

运行类

package com.weishiyao.learn.day.singleton.ep;
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
t.start();
t.start();
t.start();
t.start();
t.start();
}
}

   

运行结果:

1 1678885403
2 1678885403
3 1678885403
4 1678885403
5 1678885403
6 1678885403
7 1678885403
8 1678885403
9 1678885403
10 1678885403
11 1678885403
12 1678885403
13 1678885403
14 1678885403
15 1678885403
16 1678885403
17 1678885403
18 1678885403
19 1678885403
20 1678885403
21 1678885403
22 1678885403
23 1678885403
24 1678885403
25 1678885403

通过静态代码块只执行一次的特性也成功的得到了线程安全的单例模式

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

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

MyObject类

package com.weishiyao.learn.day.singleton.ep;
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:mysql://...:/wechat_?useUnicode=true&characterEncoding=UTF-";
String name = "root";
String password = "";
String driverName = "com.mysql.jdbc.Driver";
Class.forName(driverName);
connection = DriverManager.getConnection(url, name, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public Connection getConnection() {
return connection;
}
}

   

线程类

package com.weishiyao.learn.day.singleton.ep;
public class MyThread extends Thread {
@Override
public void run() {
for (int i = ; i < ; i++) {
System.out.println(MyObject.connectionFactory.getConnection().hashCode());
}
}
}

   

运行类

package com.weishiyao.learn.day.singleton.ep;
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
MyThread t = new MyThread();
t.start();
t.start();
t.start();
t.start();
t.start();
}
}

   

运行结果

1 调用了MyObject的构造
2 56823666
3 56823666
4 56823666
5 56823666
6 56823666
7 56823666
8 56823666
9 56823666
10 56823666
11 56823666
12 56823666
13 56823666
14 56823666
15 56823666
16 56823666
17 56823666
18 56823666
19 56823666
20 56823666
21 56823666
22 56823666
23 56823666
24 56823666
25 56823666
26 56823666

上面这种写法将枚举类暴露了,违反了“职责单一原则”,可以使用一个类将枚举包裹起来

package com.weishiyao.learn.day.singleton.ep;
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:mysql://...:/wechat_?useUnicode=true&characterEncoding=UTF-";
String name = "root";
String password = "";
String driverName = "com.mysql.jdbc.Driver";
Class.forName(driverName);
connection = DriverManager.getConnection(url, name, 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 com.weishiyao.learn.day.singleton.ep;
public class MyThread extends Thread {
@Override
public void run() {
for (int i = ; i < ; i++) {
System.out.println(MyObject.getConnection().hashCode());
}
}
}

   

结果

1 调用了MyObject的构造
2 1948356121
3 1948356121
4 1948356121
5 1948356121
6 1948356121
7 1948356121
8 1948356121
9 1948356121
10 1948356121
11 1948356121
12 1948356121
13 1948356121
14 1948356121
15 1948356121
16 1948356121
17 1948356121
18 1948356121
19 1948356121
20 1948356121
21 1948356121
22 1948356121
23 1948356121
24 1948356121
25 1948356121
26 1948356121

以上总结了单利模式与多线程结合时遇到的各种情况和解决方案,以供以后使用时查阅。

更多Java单利模式与多线程总结归纳相关文章请关注PHP中文网!


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
是否有任何威脅或增強Java平台獨立性的新興技術?是否有任何威脅或增強Java平台獨立性的新興技術?Apr 24, 2025 am 12:11 AM

新興技術對Java的平台獨立性既有威脅也有增強。 1)雲計算和容器化技術如Docker增強了Java的平台獨立性,但需要優化以適應不同雲環境。 2)WebAssembly通過GraalVM編譯Java代碼,擴展了其平台獨立性,但需與其他語言競爭性能。

JVM的實現是什麼,它們都提供了相同的平台獨立性?JVM的實現是什麼,它們都提供了相同的平台獨立性?Apr 24, 2025 am 12:10 AM

不同JVM實現都能提供平台獨立性,但表現略有不同。 1.OracleHotSpot和OpenJDKJVM在平台獨立性上表現相似,但OpenJDK可能需額外配置。 2.IBMJ9JVM在特定操作系統上表現優化。 3.GraalVM支持多語言,需額外配置。 4.AzulZingJVM需特定平台調整。

平台獨立性如何降低發展成本和時間?平台獨立性如何降低發展成本和時間?Apr 24, 2025 am 12:08 AM

平台獨立性通過在多種操作系統上運行同一套代碼,降低開發成本和縮短開發時間。具體表現為:1.減少開發時間,只需維護一套代碼;2.降低維護成本,統一測試流程;3.快速迭代和團隊協作,簡化部署過程。

Java的平台獨立性如何促進代碼重用?Java的平台獨立性如何促進代碼重用?Apr 24, 2025 am 12:05 AM

Java'splatformindependencefacilitatescodereusebyallowingbytecodetorunonanyplatformwithaJVM.1)Developerscanwritecodeonceforconsistentbehavioracrossplatforms.2)Maintenanceisreducedascodedoesn'tneedrewriting.3)Librariesandframeworkscanbesharedacrossproj

您如何在Java應用程序中對平台特定問題進行故障排除?您如何在Java應用程序中對平台特定問題進行故障排除?Apr 24, 2025 am 12:04 AM

要解決Java應用程序中的平台特定問題,可以採取以下步驟:1.使用Java的System類查看系統屬性以了解運行環境。 2.利用File類或java.nio.file包處理文件路徑。 3.根據操作系統條件加載本地庫。 4.使用VisualVM或JProfiler優化跨平台性能。 5.通過Docker容器化確保測試環境與生產環境一致。 6.利用GitHubActions在多個平台上進行自動化測試。這些方法有助於有效地解決Java應用程序中的平台特定問題。

JVM中的類加載程序子系統如何促進平台獨立性?JVM中的類加載程序子系統如何促進平台獨立性?Apr 23, 2025 am 12:14 AM

類加載器通過統一的類文件格式、動態加載、雙親委派模型和平台無關的字節碼,確保Java程序在不同平台上的一致性和兼容性,實現平台獨立性。

Java編譯器會產生特定於平台的代碼嗎?解釋。Java編譯器會產生特定於平台的代碼嗎?解釋。Apr 23, 2025 am 12:09 AM

Java編譯器生成的代碼是平台無關的,但最終執行的代碼是平台特定的。 1.Java源代碼編譯成平台無關的字節碼。 2.JVM將字節碼轉換為特定平台的機器碼,確保跨平台運行但性能可能不同。

JVM如何處理不同操作系統的多線程?JVM如何處理不同操作系統的多線程?Apr 23, 2025 am 12:07 AM

多線程在現代編程中重要,因為它能提高程序的響應性和資源利用率,並處理複雜的並發任務。 JVM通過線程映射、調度機制和同步鎖機制,在不同操作系統上確保多線程的一致性和高效性。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),