单例模式(多例,线程安全)

单例模式的本质是控制实例的个数,例如限制代码中的实例个数为确定的n个,一般情况下为1个。看代码:

class ConnectionManager {
    private static ConnectionManager manager = new ConnectionManager();
    private ConnectionManager() {
    }

    public static ConnectionManager getInstance() {
        return manager;
    }
}

这种属于“饱汉式”的单例模式,不管你什么时候调用,当加载这个类的时候,就初始化了该类的唯一对象。这种写法是线程安全的。在下面的这种情况下,JVM隐含的为您执行的同步。
1 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
2 访问final字段时

class ConnectionManager {
    private static ConnectionManager manager = null;
    private ConnectionManager() {
    }

    public static ConnectionManager getInstance() {
    if(manager==null){
        manager = new ConnectionManager();
    }
        return manager;
    }
}

这种写法属于“饿汉式”的单例模式,只有当需要的时候才去创建。当然,上述代码不是线程安全的。(既不是静态字段,也不是static{}字段),下面给出线程安全的代码:(用synchronized锁住ConnectionManager,表明一个时刻只能有一个线程访问ConnectionManager这个类 ,volatile修饰manager表明当manager被其他线程修改了,所有的线程都可以看到)

class ConnectionManager {
    private volatile static ConnectionManager manager = null;
    private ConnectionManager() {
    }

    public static ConnectionManager getInstance() {
            if(manager==null){
                synchronized(ConnectionManager.class){
                if(manager==null)
                manager = new ConnectionManager();
            }
        }
        return manager;
    }
}

下面将给出一个多例模式。

package com.guo.task;
/**
 * 显示如何扩展单例模式,控制单例的个数为3个
 * @author guo
 */
public class OneExtend{
    //定义一个缺省的key值的前缀
    private final static String DEFAULT_PREKEY="Cache";
    //定义缓存实例的容器
    private static Map<String,OneExtend> map=new HashMap<String,OneExtend>();
    //用来记录当前正在使用第几个实例,到了控制的最大输入,就返回从1开始
    private static int num=1;
    //定义最大的实例的数目
    private final static int NUM_MAX=3;

    public static OneExtend getInstance(){
        String key=DEFAULT_PREKEY+num;
        OneExtend oneExtend=map.get(key);
        if(oneExtend==null){
            oneExtend =new OneExtend();
            map.put(key,oneExtend);
        }
        //把当前的实例序号加1
        num++;
        if(num>NUM_MAX){
            num=1;
        }
        return oneExtend;
    }
}

当然,上述代码也不是线程安全的。具体的修改方式就留给大家来改吧。下面将给出一段强大的代码。该代码用于模拟数据库连接。比如假设有5个数据库连接,但是同时可能有8个人来获取连接。获取到连接的人,进行自己的业务操作,没获取到连接的人,则等待直到有人释放了连接,然后获取连接,处理自己的业务。

package com.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiModle {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ExecutorService exec = Executors.newCachedThreadPool();

        Runnable runnable = new Runnable() {

            public void run() {
                try {
                    Connection con = ConnectionManager.getInstance()
                            .getConnection();
                    System.out.println(Thread.currentThread().getId()
                            + "获取到连接.." + con);
                    System.out.println("使用连接中..");
                    Thread.sleep(1000);//模拟业务操作
                    ConnectionManager.getInstance().releaseConnection(con);
                    System.out.println(Thread.currentThread().getId()
                            + "使用结束,释放连接" + con);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (NotFoundException e) {
                    e.printStackTrace();
                }

            }
        };
        for (int i = 0; i < 8; i++) {
            exec.execute(runnable);
        }
        exec.shutdown();
    }

}

class NotFoundException extends Exception {
    public NotFoundException() {
        super("the connection is not founded.");
    }
}

class Connection {
    boolean isUsed;
    private int id;

    public Connection(int id) {
        this.id = id;
    }

    public String toString() {
        return id + "号连接";
    }
}

class ConnectionManager {
    private static ConnectionManager manager = new ConnectionManager();
    private int num = 5;
    private List<Connection> list;

    private ConnectionManager() {
        list = new ArrayList<Connection>();
        for (int i = 0; i < num; i++) {
            list.add( new Connection(i));
        }
    }

    public static ConnectionManager getInstance() {
        return manager;
    }

    public synchronized Connection getConnection() throws InterruptedException {
        Connection con = null;
        while (con == null) {
            for (int i = 0; i < num; i++) {
                if (!list.get(i).isUsed) {
                    con = list.get(i);
                    con.isUsed=true;
                    break;
                }
            }
            if (con == null) {
                System.out.println(Thread.currentThread().getId()
                        + "号线程正在等待...");
                wait();
            }
        }
        return con;
    }

    public synchronized void releaseConnection(Connection con)
            throws NotFoundException {
        for (int i = 0; i < num; i++) {
            if (list.get(i) == con) {
                con.isUsed = false;
                notifyAll();
                return;
            }
        }
        throw new NotFoundException();
    }
}

运行结果如下所示:

10获取到连接..0号连接
使用连接中..
13获取到连接..1号连接
使用连接中..
16获取到连接..2号连接
使用连接中..
12获取到连接..4号连接
使用连接中..
11号线程正在等待...
15获取到连接..3号连接
使用连接中..
17号线程正在等待...
14号线程正在等待...
10使用结束,释放连接0号连接
12使用结束,释放连接4号连接
15使用结束,释放连接3号连接
16使用结束,释放连接2号连接
14获取到连接..0号连接
使用连接中..
13使用结束,释放连接1号连接
17获取到连接..1号连接
使用连接中..
11获取到连接..2号连接
使用连接中..
11使用结束,释放连接2号连接
14使用结束,释放连接0号连接
17使用结束,释放连接1号连接
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值