Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/392.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angularjs/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 线程安全多通模式_Java_Design Patterns_Multiton - Fatal编程技术网

Java 线程安全多通模式

Java 线程安全多通模式,java,design-patterns,multiton,Java,Design Patterns,Multiton,受a的启发,我尝试创建multiton模式的线程安全实现,它依赖于唯一的键并对它们执行锁定(我从JB Nizet的答案中得到了这个想法) 问题 我提供的实现是否可行 我对Multiton(或Singleton)是否通常是好的模式不感兴趣,这将导致一场讨论。我只是想要一个干净的和有效的实现 相反: 您必须知道在编译时要创建多少实例 专业人士 不锁定整个班级或整个地图。可以同时调用getInstance 通过key对象获取实例,而不仅仅是无界的int或String,因此您可以确保在方法调用后获

受a的启发,我尝试创建multiton模式的线程安全实现,它依赖于唯一的键并对它们执行锁定(我从JB Nizet的答案中得到了这个想法)

问题

我提供的实现是否可行

我对Multiton(或Singleton)是否通常是好的模式不感兴趣,这将导致一场讨论。我只是想要一个干净的和有效的实现

相反

  • 您必须知道在编译时要创建多少实例
专业人士

  • 不锁定整个班级或整个地图。可以同时调用
    getInstance
  • 通过key对象获取实例,而不仅仅是无界的
    int
    String
    ,因此您可以确保在方法调用后获得非空实例
  • 线程安全(至少这是我的印象)

公共类Multiton
{
私有静态最终映射,Multiton>();
私有Multiton(){System.out.println(“已创建实例”);}
/*可以并发调用,因为它只在id上同步*/

publicstatic我不是Java程序员,但是:
HashMap
对于并发访问是不安全的。我可以推荐
ConcurrentHashMap

  private static final ConcurrentHashMap<Object, Multiton> instances = new ConcurrentHashMap<Object, Multiton>();

  public static <TYPE extends Object, KEY extends Enum<Keys> & MultitionKey<TYPE>> Multiton getInstance(KEY id)
  {
    Multiton result;
    synchronized (id)
    {
      result = instances.get(id);
      if(result == null)
      {
        result = new Multiton();
        instances.put(id, result);
      }
    }
    System.out.println("Retrieved instance.");
    return result;
  }
私有静态最终ConcurrentHashMap实例=新ConcurrentHashMap();
公共静态Multiton getInstance(密钥id)
{
多子结果;
已同步(id)
{
result=instances.get(id);
如果(结果==null)
{
结果=新的Multiton();
实例.put(id,result);
}
}
System.out.println(“检索到的实例”);
返回结果;
}
我认为不可行

id
参数上进行同步充满了危险-如果他们将此
enum
用于另一个同步机制会怎么样?当然
HashMap
并非如评论所指出的那样是并发的

要演示-请尝试以下操作:

Runnable r = new Runnable() {
  @Override
  public void run() { 
    // Added to demonstrate the problem.
    synchronized(Keys.KEY_1) {
      getInstance(Keys.KEY_1);
    } 
  }
};
这是一个使用原子而不是同步的实现,因此应该更有效。它比您的实现复杂得多,但在
Miltiton
中处理所有边缘情况是复杂的

public class Multiton {
  // The static instances.
  private static final AtomicReferenceArray<Multiton> instances = new AtomicReferenceArray<>(1000);

  // Ready for use - set to false while initialising.
  private final AtomicBoolean ready = new AtomicBoolean();
  // Everyone who is waiting for me to initialise.
  private final Queue<Thread> waiters = new ConcurrentLinkedQueue<>();
  // For logging (and a bit of linguistic fun).
  private final int forInstance;

  // We need a simple constructor.
  private Multiton(int forInstance) {
    this.forInstance = forInstance;
    log(forInstance, "New");
  }

  // The expensive initialiser.
  public void init() throws InterruptedException {
    log(forInstance, "Init");
    // ... presumably heavy stuff.
    Thread.sleep(1000);

    // We are now ready.
    ready();
  }

  private void ready() {
    log(forInstance, "Ready");
    // I am now ready.
    ready.getAndSet(true);
    // Unpark everyone waiting for me.
    for (Thread t : waiters) {
      LockSupport.unpark(t);
    }
  }

  // Get the instance for that one.
  public static Multiton getInstance(int which) throws InterruptedException {
    // One there already?
    Multiton it = instances.get(which);
    if (it == null) {
      // Lazy make.
      Multiton newIt = new Multiton(which);
      // Successful put?
      if (instances.compareAndSet(which, null, newIt)) {
        // Yes!
        it = newIt;
        // Initialise it.
        it.init();
      } else {
        // One appeared as if by magic (another thread got there first).
        it = instances.get(which);
        // Wait for it to finish initialisation.
        // Put me in its queue of waiters.
        it.waiters.add(Thread.currentThread());
        log(which, "Parking");
        while (!it.ready.get()) {
          // Park me.
          LockSupport.park();
        }
        // I'm not waiting any more.
        it.waiters.remove(Thread.currentThread());
        log(which, "Unparked");
      }
    }

    return it;
  }

  // Some simple logging.
  static void log(int which, String s) {
    log(new Date(), "Thread " + Thread.currentThread().getId() + " for Multiton " + which + " " + s);
  }
  static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
  // synchronized so I don't need to make the DateFormat ThreadLocal.

  static synchronized void log(Date d, String s) {
    System.out.println(dateFormat.format(d) + " " + s);
  }

  // The tester class.
  static class MultitonTester implements Runnable {
    int which;

    private MultitonTester(int which) {
      this.which = which;
    }

    @Override
    public void run() {
      try {
        Multiton.log(which, "Waiting");
        Multiton m = Multiton.getInstance(which);
        Multiton.log(which, "Got");
      } catch (InterruptedException ex) {
        Multiton.log(which, "Interrupted");
      }
    }
  }

  public static void main(String[] args) throws InterruptedException {
    int testers = 50;
    int multitons = 50;
    // Do a number of them. Makes n testers for each Multiton.
    for (int i = 1; i < testers * multitons; i++) {
      // Which one to create.
      int which = i / testers;
      //System.out.println("Requesting Multiton " + i);
      new Thread(new MultitonTester(which+1)).start();
    }

  }
}
公共类Multiton{
//静态实例。
私有静态最终AtomicReferenceArray实例=新的AtomicReferenceArray(1000);
//准备使用-初始化时设置为false。
private final AtomicBoolean ready=新的AtomicBoolean();
//每一个等待我初始化的人。
private final Queue waiters=new ConcurrentLinkedQueue();
//记录日志(还有一点语言乐趣)。
例如,私人机构;
//我们需要一个简单的构造函数。
专用多音频(例如int){
this.forInstance=例如;
日志(例如,“新”);
}
//昂贵的初始化器。
public void init()引发InterruptedException{
日志(例如,“Init”);
//…大概是很重的东西。
睡眠(1000);
//我们现在准备好了。
ready();
}
私有void ready(){
日志(例如,“就绪”);
//我现在准备好了。
ready.getAndSet(true);
//让每个人都等我。
用于(线程t:服务员){
锁支持。unpark(t);
}
}
//获取该实例。
公共静态Multiton getInstance(int)引发InterruptedException{
//已经有一个了?
Multiton it=instances.get(哪个);
if(it==null){
//懒惰制造。
Multiton newIt=新的Multiton(哪个);
//成功的看跌期权?
if(instances.compareAndSet(which、null、newIt)){
//对!!
it=newIt;
//初始化它。
it.init();
}否则{
//其中一条似乎是魔术般出现的(另一条线先到了那里)。
it=instances.get(which);
//等待它完成初始化。
//把我放在服务员的队伍里。
添加(Thread.currentThread());
日志(即“停车场”);
而(!it.ready.get()){
//让我停下来。
锁支架。驻车();
}
//我不再等了。
it.waiters.remove(Thread.currentThread());
日志(即“未切割”);
}
}
归还它;
}
//一些简单的日志记录。
静态无效日志(整数,字符串s){
日志(新日期(),“线程”+线程.currentThread();
}
静态最终日期格式DateFormat=新的SimpleDataFormat(“yyyy-MM-dd HH:MM:ss.SSS”);
//已同步,因此不需要将DateFormat设置为本地。
静态同步作废日志(日期d,字符串s){
System.out.println(dateFormat.format(d)+“”+s);
}
//tester类。
静态类MultiContester实现可运行{
int其中;
专用多端口(int-which){
this.which=哪个;
}
@凌驾
公开募捐{
试一试{
Multiton.log(即“等待”);
Multiton m=Multiton.getInstance(which);
Multiton.log(其中“Got”);
}捕获(中断异常例外){
Multiton.log(其中“中断”);
}
}
}
公共静态void main(字符串[]args)引发InterruptedException{
int测试员=50;
int多声子=50;
//做一些测试。为每个Multiton制作n个测试仪。
对于(int i=1;i
它绝对不是线程安全的。下面是一个简单的例子,说明了许多可能出错的事情

线程A正试图在键
id1
处放置。由于在
id2
处放置,线程B正在调整存储桶表的大小。因为这些存储桶具有不同的同步监视器,所以它们将并行地退出比赛

Thread A                      Thread B
--------                      --------
b = key.hash % map.buckets.size   

                             copy map.buckets reference to local var
                             set map.buckets = new Bucket[newSize]
                             insert keys from old buckets into new buckets

insert into map.buckets[b]
在本例中,假设
线程A
锯t
public class Multiton {
  // The static instances.
  private static final AtomicReferenceArray<Multiton> instances = new AtomicReferenceArray<>(1000);

  // Ready for use - set to false while initialising.
  private final AtomicBoolean ready = new AtomicBoolean();
  // Everyone who is waiting for me to initialise.
  private final Queue<Thread> waiters = new ConcurrentLinkedQueue<>();
  // For logging (and a bit of linguistic fun).
  private final int forInstance;

  // We need a simple constructor.
  private Multiton(int forInstance) {
    this.forInstance = forInstance;
    log(forInstance, "New");
  }

  // The expensive initialiser.
  public void init() throws InterruptedException {
    log(forInstance, "Init");
    // ... presumably heavy stuff.
    Thread.sleep(1000);

    // We are now ready.
    ready();
  }

  private void ready() {
    log(forInstance, "Ready");
    // I am now ready.
    ready.getAndSet(true);
    // Unpark everyone waiting for me.
    for (Thread t : waiters) {
      LockSupport.unpark(t);
    }
  }

  // Get the instance for that one.
  public static Multiton getInstance(int which) throws InterruptedException {
    // One there already?
    Multiton it = instances.get(which);
    if (it == null) {
      // Lazy make.
      Multiton newIt = new Multiton(which);
      // Successful put?
      if (instances.compareAndSet(which, null, newIt)) {
        // Yes!
        it = newIt;
        // Initialise it.
        it.init();
      } else {
        // One appeared as if by magic (another thread got there first).
        it = instances.get(which);
        // Wait for it to finish initialisation.
        // Put me in its queue of waiters.
        it.waiters.add(Thread.currentThread());
        log(which, "Parking");
        while (!it.ready.get()) {
          // Park me.
          LockSupport.park();
        }
        // I'm not waiting any more.
        it.waiters.remove(Thread.currentThread());
        log(which, "Unparked");
      }
    }

    return it;
  }

  // Some simple logging.
  static void log(int which, String s) {
    log(new Date(), "Thread " + Thread.currentThread().getId() + " for Multiton " + which + " " + s);
  }
  static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
  // synchronized so I don't need to make the DateFormat ThreadLocal.

  static synchronized void log(Date d, String s) {
    System.out.println(dateFormat.format(d) + " " + s);
  }

  // The tester class.
  static class MultitonTester implements Runnable {
    int which;

    private MultitonTester(int which) {
      this.which = which;
    }

    @Override
    public void run() {
      try {
        Multiton.log(which, "Waiting");
        Multiton m = Multiton.getInstance(which);
        Multiton.log(which, "Got");
      } catch (InterruptedException ex) {
        Multiton.log(which, "Interrupted");
      }
    }
  }

  public static void main(String[] args) throws InterruptedException {
    int testers = 50;
    int multitons = 50;
    // Do a number of them. Makes n testers for each Multiton.
    for (int i = 1; i < testers * multitons; i++) {
      // Which one to create.
      int which = i / testers;
      //System.out.println("Requesting Multiton " + i);
      new Thread(new MultitonTester(which+1)).start();
    }

  }
}
Thread A                      Thread B
--------                      --------
b = key.hash % map.buckets.size   

                             copy map.buckets reference to local var
                             set map.buckets = new Bucket[newSize]
                             insert keys from old buckets into new buckets

insert into map.buckets[b]