常用设计模式

单例设计模式

单例设计模式一种对象创建模式,用于产生一个对象的具体实例,它可以确保系统中一个类只产生一个实例,其优势主要有以下两点:

  • 可以节省new操作的时间花费
  • 由于减少了new操作的频率,因此降低了内存使用频率,从而减轻了垃圾回收器的压力
1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
//将构造函数设置为private,如果不是private,则instance安全性无法保障
private Singleton(){
System.out.println("Singleton is created");
}

private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}

上述方法有个问题就是在Singleton构造函数中,Singleton实例的创建是不受控制的,对于静态成员instance,它会在类第一次初始化的时候被创建。这个时刻并不一定是getInstance()方法第一次被调用的时候。要解决上述问题,就需要利用一种延迟加载策略,它只会在instance第一次使用时创建对象。

1
2
3
4
5
6
7
8
9
10
11
12
public class LazySingleton(){
private LazySingleton(){
System.out.println("LazySingleton is created");
}
private static LazySingleton instance = null;
public static synchronized LazySingleton getInstance(){
if (instance == null){
instance = new LazySingleton();
}
return instance;
}
}

还有一种方式结合了上述两种方式的优势,既可以去掉锁使得性能提升,又只会在getInstance()方法第一次调用的时候创建实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
pulibc class StaticSingleton{
private StaticSingleton(){
System.out.println("StaticSingleton is created");
}

private static class SingletonHolder{
private static StaticSingleton instance = new StaticSingleton();
}

public static StaticSingleton getInstance(){
return SingletonHolder.instance;
}
}

常见的懒汉式(线程安全与不安全)、饿汉式和静态内部类的写法

  • 懒汉式(线程不安全)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {

private static Singleton singleton;

private Singleton() {
}

public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
  • 懒汉式(线程安全)
1
2
3
4
5
6
7
8
9
10
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • 饿汉式
1
2
3
4
5
6
7
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
  • 静态内部类
1
2
3
4
5
6
7
8
9
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

不变模式

在并发编程过程中,需要进行同步操作保证数据的一致性,但是使用同步操作又会降低性能,不变模式利用对象的不变性,可以在没有同步操作的情况下依然保证数据的一致性和正确性。不变模式主要有以下两个特点:

  • 当对象被创建后,其内部状态和数据不再发生任何变化
  • 对象需要被共享,被多线程频繁访问
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public final class Product {
private final String no;
private final String name;
private final double price;

public Product(String no, String name, double price) {
super();
this.no = no;
this.name = name;
this.price = price;
}

public String getNo() {
return no;
}

public String getName() {
return name;
}

public double getPrice() {
return price;
}
}

生产者-消费者模式

在生产者-消费者模式中,通常有两类线程,即若干个生产者线程和若干个消费者线程。生产者线程负责提交用户请求,消费者线程则负责具体处理生产者提交的任务。生产者和消费者之间则通过共享内存缓冲区进行通信。

生产者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Producer implements Runnable {
private volatile boolean isRunning = true;
private BlockingQueue<PCData> queue;
private static AtomicInteger count = new AtomicInteger();
private static final int SLEEPTIME = 1000;

public Producer(BlockingQueue<PCData> queue) {
this.queue = queue;
}

@Override
public void run() {
PCData data = null;
Random r = new Random();
System.out.println("start producer id = " + Thread.currentThread().getId());
try {
while (isRunning) {
Thread.sleep(r.nextInt(SLEEPTIME));
data = new PCData(count.incrementAndGet());
System.out.println(data + " is put into queue");
if (!queue.offer(data, 2, TimeUnit.SECONDS)) {
System.out.println("failed to put data: " + data);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}

public void stop() {
isRunning = false;
}
}

消费者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Consumer implements Runnable {
private BlockingQueue<PCData> queue;
private static final int SLEEPTIME = 1000;

public Consumer(BlockingQueue<PCData> queue) {
this.queue = queue;
}

@Override
public void run() {
System.out.println("start Consumer id = " + Thread.currentThread().getId());
Random r = new Random();
try {
while (true) {
PCData data = queue.take();
if (null != data) {
int re = data.getData() * data.getData();
System.out.println(MessageFormat.format("{0}*{1}={2}", data.getData(), data.getData(), re));
Thread.sleep(r.nextInt(SLEEPTIME));
}
}
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}

资源(共享数据模型):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public final class PCData {
private final int intData;
public PCData(int d){
intData = d;
}

public PCData(String d){
intData = Integer.valueOf(d);
}

public int getData(){
return intData;
}

@Override
public String toString() {
return "data: " + intData;
}
}

由于使用BlockingQueue导致性能不佳,提出了另一种改进措施:使用Disruptor框架,Disruptor框架利用环形队列实现高效的无锁内存队列。队列是环形的,则只需要对外提供一个当前位置cursor,利用这个指针既可以进行入队操作,也可以进行出队操作。由于环形队列的缘故,队列的总大小必须事先指定,不能动态扩展。这种固定大小的环形队列的另外一个好处就是可以做到完全的内存复用。在系统的运行过程中,不会有新的空间需要分配或者老的空间需要回收,大大减少系统分配空间及回收空间的额外开销。

资源:

1
2
3
4
5
6
7
8
9
10
11
public class PCData {
private long value;

public void set(long value) {
this.value = value;
}

public long get() {
return value;
}
}

工厂类:

1
2
3
4
5
6
//它会在Disruptor框架系统初始化时,构造所有的缓冲区中的对象实例
public class PCDataFactory implements EventFactory<PCData> {
public PCData newInstance(){
return new PCData();
}
}

消费者:

1
2
3
4
5
6
7
public class Consumer implements WorkHandler<PCData> {
//回调方法
@Override
public void onEvent(PCData pcData) throws Exception {
System.out.println(Thread.currentThread().getId() + ":Event: -- " + pcData.get() * pcData.get() + "--");
}
}

生产者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Producer {
private final RingBuffer<PCData> ringBuffer;

public Producer(RingBuffer<PCData> ringBuffer) {
this.ringBuffer = ringBuffer;
}

public void pushData(ByteBuffer bb) {
long sequence = ringBuffer.next();
try {
PCData event = ringBuffer.get(sequence);
event.set(bb.getLong(0));
} finally {
ringBuffer.publish(sequence);
}
}
}

Future模式

当我们需要调用一个函数方法时,如果这个函数执行得很慢,那么我们就要进行等待。但有时候,我们可能并不急着要结果。因此,我们可以让被调者立即返回,让它在后台慢慢处理这个请求。对于调用者来说,则可以先处理一些其他任务,在真正需要数据的场合再去尝试获得需要的数据。

Data接口:

1
2
3
4
//该接口有两个重要的实现,一个是RealData表示真实数据,另一个是FutureData用来立即返回结果(RealData虚拟实现)
public interface Data {
String getResult();
}

FutureData:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class FutureData implements Data {
protected RealData realdata = null;
protected boolean isReady = false;

public synchronized void setRealdata(RealData realdata) {
if (isReady) {
return;
}
this.realdata = realdata;
isReady = true;
notifyAll();
}

@Override
public synchronized String getResult() {
while (!isReady) {
try {
wait();
} catch (InterruptedException e) {

}
}
return realdata.result;
}
}

RealData:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RealData implements Data {
protected final String result;

public RealData(String para) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 10; i++) {
sb.append(para);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
result = sb.toString();
}

@Override
public synchronized String getResult() {
return result;
}
}

Client:

1
2
3
4
5
6
7
8
9
10
public class Client {
public Data request(final String queryStr) {
final FutureData future = new FutureData();
new Thread(() -> {
RealData realData = new RealData(queryStr);
future.setRealdata(realData);
}).start();
return future;
}
}

Main方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String[] args) {
Client client = new Client();
Data data = client.request("name");
System.out.println("请求完毕");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {

}
System.out.println("数据 = " + data.getResult());
}
}

观察者模式(Observer Pattern)

一般有一个被观察者和多个观察者,一旦被观察者状态发生改变,就会通知到多个观察者,即当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

装饰者模式

适配器模式

工厂模式

Comments