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

      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.总结——趣味见解

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值