ホームページ  >  記事  >  Java  >  Java 単一関心モデルとマルチスレッドの概要

Java 単一関心モデルとマルチスレッドの概要

高洛峰
高洛峰オリジナル
2017-01-05 16:56:541383ブラウズ

コンセプト:

シングルトンパターンはJavaにおける一般的な設計パターンで、遅延スタイルシングルトン、ハングリースタイルシングルトン、登録スタイルシングルトンの3種類があります。

シングルトンパターンには以下の特徴があります:

1. シングルトンクラスはインスタンスを 1 つだけ持つことができます。

2. シングルトンクラスは独自の一意のインスタンスを作成する必要があります。

3. シングルトンクラスは、このインスタンスを他のすべてのオブジェクトに提供する必要があります。

シングルトンパターンは、クラスがインスタンスを 1 つだけ持つことを保証し、クラス自体をインスタンス化し、このインスタンスをシステム全体に提供します。コンピュータ システムでは、スレッド プール、キャッシュ、ログ オブジェクト、ダイアログ ボックス、プリンタ、グラフィック カード ドライバ オブジェクトはシングルトンとして設計されることがよくあります。これらのアプリケーションはすべて、多かれ少なかれリソース マネージャーの機能を備えています。各コンピュータには複数のプリンタを搭載できますが、2 つの印刷ジョブが同時にプリンタに出力されることを防ぐために、存在できるプリンタ スプーラは 1 つだけです。各コンピュータは複数の通信ポートを持つことができ、システムはこれらの通信ポートを集中管理して、1 つの通信ポートが 2 つの要求によって同時に呼び出されないようにする必要があります。つまり、シングルトン モードを選択する目的は、矛盾した状態を回避し、長期的なポリシーを回避することです。

ここでは主に怠け者の中華風と空腹の中華風の2種類を詳しく紹介します

1. 即時読み込み/空腹の中華風

メソッドを呼び出す前にインスタンスが作成されています、コード:

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

スレッドの作成class

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

実行結果

1 167772895
2 167772895
3

hashCode は同じ値であり、オブジェクトも同じであることを示します。即時ロード型が実装されていること 単一関心モード


2. 遅延ロード/遅延スタイル


メソッドが呼び出されるまでインスタンスは作成されない 実装計画では、インスタンス化を引数なしのコンストラクターに入れることができます。オブジェクトのインスタンス、コード:

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

実行クラスの作成

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

実行結果


1 1677 72895

オブジェクトのインスタンスを取り出しますが、マルチスレッド環境だとインスタンスが複数あるため、シングルトンモードではありません


テストクラスを実行します

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 980258163

2 1224717057

3 1851889404
4 188820504
5 1672864109

そこから問題があるので、それを解決する必要があります。遅延モードでのマルチスレッド ソリューションのコード:


最初の解決策は、最も一般的であり、同期を追加することであり、同期は別の場所に追加できます


最初の解決策は、ロックメソッドです

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

この同期同期スキームは、メソッド全体の効率が低すぎることにつながりますはロックされています


2 番目の同期使用スキーム

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

この方法も非常に非効率です。 3 番目の同期使用プラン

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;
}
}
のみをロックする必要があります。

このように書くのが最善の解決策のように見えますが、結果を実行したところ、実際にはスレッドセーフではないことがわかりました。 4 1224717057

5 1672864109

なぜですか?


オブジェクト作成ステートメントはロックされていますが、一度に作成を完了できるスレッドは 1 つだけですが、最初のスレッドが Object オブジェクトを作成するときに、2 番目のスレッドは引き続きオブジェクトの作成を続行できます。作成ステートメントのみがロックされます。

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



は、シングルトンを確保するために別の判断を追加するだけです。結果は次のとおりです。


1 1224717057
2 1224717057

3 1224717057

4 1224717057

5 1224717057

3. 組み込みの静的クラスを使用してシングルトンを実装します

メインコード

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


スレッドクラスコード

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();
MyThread t = new MyThread();
MyThread t = new MyThread();
t.start();
t.start();
t.start();
t.start();
t.start();
}
}

結果

1851889404

1851889404

1851889404

1851889404

1851889404

内部静的クラスを介して、スレッドセーフなシングルトンモードを取得しました

4.

組み込みの静的クラスではスレッド セーフの問題が発生する可能性がありますが、シリアル化されたオブジェクトが発生した場合、デフォルトのメソッドを使用して取得される結果は依然として複数のインスタンスです

結果

1 970928725
2 1099149023

2 つの異なる hashCode が同じオブジェクトではないことを証明します。 解決策として、次のコードを追加します

package com.weishiyao.learn.day8.singleton.ep5;
import java.io.Serializable;
public class MyObject implements Serializable {
/**
*
*/
private static final long serialVersionUID = 888L;
// 内部类方式
private static class MyObjectHandler {
private static MyObject myObject = new MyObject();
}
public MyObject() {
}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
// protected MyObject readResolve() {
// System.out.println("调用了readResolve方法!");
// return MyObjectHandler.myObject;
// }
}


逆シリアル化中に呼び出すと、同じオブジェクトを取得できます。

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

Result

1 1255301379

2 readResolve メソッドが呼び出されます。

3 1255301379

同じハッシュコードは同じオブジェクトが取得されることを証明します

5. シングルトンを実装するには静的コードブロックを使用します

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

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 までご連絡ください。