Java设计模式之一 —— 单态模式

来源:互联网 发布:中船重工集团718知乎 编辑:程序博客网 时间:2024/06/10 00:26

      1.问题的提出

      假设我们想要向数据库中的某个表中insert多条记录,insert操作可能是由多个线程并发来完成,那么此数据表中的主键ID是通过我们写的一个程序来完成数字的自增。我们来写一个类,用来取得下一个主键ID的值,并在insert操作中插入此主键ID。代码如下:

 

public class Util
{
    //取得下一次插入数据表中的主键ID
    public static Integer getNextTableId()
    {
        //取得数据表中当前主键ID
        //代码略
        
        //返回下一此插入数据库表中的主键ID
        return tableId++;
    }
}

 

      我们再写5个线程来模拟一下主键ID生成器,代码如下:

 

public class ThreadSingleton extends Thread
{
    private boolean    active;
    private long        millis;
    private String        myThreadId;
    
    public ThreadSingleton(long ms, String threadId)
    {
        this.millis         = ms;
        this.active         = true;
        this.myThreadId     = threadId;
    }
    
    public void run()
    {
        while (this.active)
        {
            Singleton.printOrder();
            try
            {
                super.sleep(this.millis);
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}

public class Client
{
    public static void main(String[] args) throws InterruptedException
    {
        long delay = 100;        
        ThreadSingleton mt1 = new ThreadSingleton(delay,"001");
        mt1.start();
        ThreadSingleton mt2 = new ThreadSingleton(delay,"002");
        mt2.start();        
        ThreadSingleton mt3 = new ThreadSingleton(delay,"003");
        mt3.start();        
        ThreadSingleton mt4 = new ThreadSingleton(delay,"004");
        mt4.start();        
        ThreadSingleton mt5 = new ThreadSingleton(delay,"005");
        mt5.start();        
    }
}

 

      于是问题产生了,在开发级测试中,我们通过多线程并发取得需要插入的主键ID,并向此数据表中insert内容,几乎不会有问题,然而在企业级测试中,例如1000个人或更多的人同时对此数据表进行insert操作的时候,就会出现主键ID重复错误和顺序颠倒。模拟运行结果如下:

 

 

      原因就是我们在通过 getNextTableId()方法取得下一次需要插入的主键ID时,由于1000乃至更多的线程同时并发, getNextTableId()方法取得TableID可能会出现相同的情况,也就是说可能存在某一个时刻,两个进程同时取得的TableID为100,那么此方法返回的结果同时为101,那么我们向数据库中插入的两条数据的主键ID同时为101,结果出错。

      2.解决问题

      我们想解决上面的问题,首先需要一个对象或方法,无论有多少个线程去访问,在同一个时刻,只能有一个对象或方法被使用。那么在java里面我们可以采用synchronized关键字来保证一个类只允许存在一个实例,并提供一个可以访问它的全局访问点。

 

      3.类图设计

      4.代码编写

 

//单态模式
public class Singleton
{
    //定义一个Singleton类的对象
    private static Singleton    instance    = null;
    
    //进行主键ID自增模拟
    private static int            order        = 1;
    
    //定义一个生成唯一一个Singleton对象的方法
    public static synchronized Singleton getInstance()
    {
        //如果instance为空
        if (instance == null)
        {
            //new一个Singleton对象,保证只生成一个instance对象
            instance = new Singleton();
        }
        
        //返回instance对象
        return instance;
    }
    
    //模拟主键自增的测试方法
    public static synchronized void printOrder()
    {
        //向控制台输出主键ID
        System.out.println(order++);
    }
}

 

      由于给getInstance()方法和 printOrder()方法加上了synchronized关键字,那么在多线程访问它们时,在同一时刻只会生成同一个Singleton实例。程序的运行结果如下:

 

 

      运行结果正确,不在出现ID重复和顺序颠倒的错误。

 

      5.总结——趣味见解

      我们很多朋友到一家比较小的餐馆去喝酒,这家餐馆只有一个厨师。我们点了很多很多的菜,但是无论我们点多少,只能通过这一个厨师来掌勺。虽然有点慢,但是也没办法。

原创粉丝点击