পুনরাবৃত্তি সমান্তরাল হ্যাশম্যাপ মান থ্রেড নিরাপদ?


156

জন্য javadoc সালে ConcurrentHashMap নিম্নলিখিত হল:

পুনরুদ্ধার অপারেশন (get সহ) সাধারণত ব্লক করে না, সুতরাং আপডেট অপারেশনগুলির সাথে ওভারল্যাপ হতে পারে (পুট এবং রিমুভ সহ)। পুনরুদ্ধারগুলি সর্বশেষে সম্পন্ন হওয়া আপডেট অপারেশনের ফলাফলগুলি তাদের সূত্রপাতটি ধরে রাখে reflect পুটআল এবং ক্লিয়ার মতো সামগ্রিক ক্রিয়াকলাপের জন্য, সমবর্তী পুনরুদ্ধারগুলি কেবলমাত্র কিছু এন্ট্রি সন্নিবেশ বা অপসারণকে প্রতিফলিত করতে পারে। একইভাবে, আইট্রেটারস এবং গণনাগুলি পুনরুক্তি / গণনার তৈরির সময় বা তার পরে কোনও সময় হ্যাশ টেবিলের অবস্থা প্রতিফলিত করে এমন উপাদানগুলি ফেরত দেয়। তারা সমকালীন মডিডিফিকেশনটি ছুঁড়ে না। যাইহোক, পুনরাবৃত্তিগুলি একবারে কেবল একটি থ্রেড দ্বারা ব্যবহার করার জন্য ডিজাইন করা হয়।

এর মানে কী? যদি আমি একই সাথে দুটি থ্রেড সহ মানচিত্রটি পুনরাবৃত্তি করার চেষ্টা করি তবে কী হবে? আমি মানচিত্রটি পুনরাবৃত্ত করার সময় মানটি রেখে দিলে বা সরিয়ে ফেললে কী হবে?

উত্তর:


193

এর মানে কী?

এর অর্থ হ'ল আপনি যা থেকে প্রাপ্ত প্রতিটি পুনরায় ConcurrentHashMapএকক থ্রেড দ্বারা ডিজাইনের জন্য ডিজাইন করা হয়েছে এবং এটি পাস করা উচিত নয়। এতে প্রতিটি লুপের জন্য সরবরাহ করা সিনট্যাকটিক চিনি অন্তর্ভুক্ত রয়েছে।

যদি আমি একই সাথে দুটি থ্রেড সহ মানচিত্রটি পুনরাবৃত্তি করার চেষ্টা করি তবে কী হবে?

এটি প্রত্যাশার মতো কাজ করবে যদি প্রতিটি থ্রেড তার নিজস্ব পুনরুক্তি ব্যবহার করে।

আমি মানচিত্রটি পুনরাবৃত্ত করার সময় মানটি রেখে দিলে বা সরিয়ে ফেললে কী হবে?

এটি গ্যারান্টিযুক্ত যে আপনি যদি এটি করেন তবে জিনিসগুলি ভাঙবে না (এটি "সমবর্তী" এর ConcurrentHashMapঅর্থ যা তার অংশ )। তবে, কোনও থ্রেড অন্য থ্রেডের যে মানচিত্রের পরিবর্তনগুলি দেখবে তা মানচিত্রের (মানচিত্র থেকে নতুন পুনরাবৃত্তি প্রাপ্ত না করে) দেখতে পাবে এমন কোনও গ্যারান্টি নেই। পুনরুক্তি মানচিত্রটি তৈরির সময় তার স্থিতি প্রতিফলিত করার গ্যারান্টিযুক্ত। ভবিষ্যতের পরিবর্তনগুলি পুনরাবৃত্তির মধ্যে প্রতিফলিত হতে পারে, তবে সেগুলি হওয়ার দরকার নেই।

উপসংহারে, একটি বিবৃতি মত

for (Object o : someConcurrentHashMap.entrySet()) {
    // ...
}

আপনি যখনই এটি দেখেন প্রায় প্রতিবারই ভাল (বা কমপক্ষে নিরাপদ) হয়ে যাবে।


সুতরাং যদি পুনরাবৃত্তি চলাকালীন, অন্য থ্রেডটি মানচিত্র থেকে কোনও বস্তু o10 সরিয়ে দেয়? এটি পুনরুক্তি করা সত্ত্বেও আমি কি পুনরাবৃত্তিতে o10 দেখতে পাচ্ছি? @ ওয়ালধেঞ্জ
অ্যালেক্স

উপরে উল্লিখিত হিসাবে, কোনও বিদ্যমান পুনরাবৃত্তকারী মানচিত্রে পরবর্তী পরিবর্তনগুলি প্রতিফলিত করবে কিনা তা সত্যই নির্দিষ্ট করা হয়নি। সুতরাং আমি জানি না, এবং স্পেসিফিকেশন অনুসারে কেউই করে না (কোডটি না দেখে, এবং এটি রানটাইমের প্রতিটি আপডেটের সাথে পরিবর্তিত হতে পারে)। সুতরাং আপনি এটির উপর নির্ভর করতে পারবেন না।
ওয়ালধেঞ্জ

8
কিন্তু আমি এখনও একটি পেয়েছিলাম ConcurrentModificationExceptionযখন একটি iterating ConcurrentHashMap, কেন?
কিমি চিউ

@ কিমিচিহ আপনার সম্ভবত একটি নতুন প্রশ্ন পোস্ট করা উচিত যা কোডটি ব্যতিক্রমটিকে ট্রিগার করে তবে আমি খুব সন্দেহ করি যে এটি পুনরাবৃত্তি থেকে সরাসরি একটি যুগল ধারক থেকে উদ্ভূত হয়েছে। জাভা বাস্তবায়ন বগি না হলে
ওয়ালধেঞ্জ

18

আপনি এই ক্লাসটি দুটি অ্যাক্সেসিং থ্রেড এবং একটি ভাগ করে নেওয়া ভাগ করে নেওয়ার জন্য পরীক্ষা করতে পারেন ConcurrentHashMap:

import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Map<String, String> map;

    public Accessor(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (Map.Entry<String, String> entry : this.map.entrySet())
      {
        System.out.println(
            Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']'
        );
      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Map<String, String> map;
    private final Random random = new Random();

    public Mutator(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (int i = 0; i < 100; i++)
      {
        this.map.remove("key" + random.nextInt(MAP_SIZE));
        this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
        System.out.println(Thread.currentThread().getName() + ": " + i);
      }
    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.map);
    Accessor a2 = new Accessor(this.map);
    Mutator m = new Mutator(this.map);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}

কোন ব্যতিক্রম নিক্ষেপ করা হবে না।

অ্যাকসেসর থ্রেডগুলির মধ্যে একই পুনরাবৃত্তি ভাগ করে নেওয়ার কারণে অচলাবস্থা দেখা দিতে পারে:

import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();
  private final Iterator<Map.Entry<String, String>> iterator;

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
    this.iterator = this.map.entrySet().iterator();
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Iterator<Map.Entry<String, String>> iterator;

    public Accessor(Iterator<Map.Entry<String, String>> iterator)
    {
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while(iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        try
        {
          String st = Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
        } catch (Exception e)
        {
          e.printStackTrace();
        }

      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Map<String, String> map;
    private final Random random = new Random();

    public Mutator(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (int i = 0; i < 100; i++)
      {
        this.map.remove("key" + random.nextInt(MAP_SIZE));
        this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
      }
    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.iterator);
    Accessor a2 = new Accessor(this.iterator);
    Mutator m = new Mutator(this.map);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}

Iterator<Map.Entry<String, String>>অ্যাক্সেসর এবং মিউটর থ্রেডগুলির মধ্যে একই ভাগ করা শুরু করার সাথে সাথে java.lang.IllegalStateExceptionপপিং আপ শুরু হবে।

import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();
  private final Iterator<Map.Entry<String, String>> iterator;

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
    this.iterator = this.map.entrySet().iterator();
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Iterator<Map.Entry<String, String>> iterator;

    public Accessor(Iterator<Map.Entry<String, String>> iterator)
    {
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while (iterator.hasNext())
      {
        Map.Entry<String, String> entry = iterator.next();
        try
        {
          String st =
              Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
        } catch (Exception e)
        {
          e.printStackTrace();
        }

      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Random random = new Random();

    private final Iterator<Map.Entry<String, String>> iterator;

    private final Map<String, String> map;

    public Mutator(Map<String, String> map, Iterator<Map.Entry<String, String>> iterator)
    {
      this.map = map;
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while (iterator.hasNext())
      {
        try
        {
          iterator.remove();
          this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
        } catch (Exception ex)
        {
          ex.printStackTrace();
        }
      }

    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.iterator);
    Accessor a2 = new Accessor(this.iterator);
    Mutator m = new Mutator(map, this.iterator);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}

'অ্যাকসেসর থ্রেডের মধ্যে একই পুনরায় ভাগ করা ডেডলক হতে পারে' সম্পর্কে আপনি কি নিশ্চিত? দস্তাবেজটি বলে যে পঠন করা অবরুদ্ধ নয় এবং আমি আপনার প্রোগ্রামটি চেষ্টা করেছি এবং কোনও অচলাবস্থা এখনও ঘটেনি। যদিও পুনরাবৃত্তির ফলাফলটি ভুল হবে।
টনি

12

এর অর্থ হ'ল একাধিক থ্রেডের মধ্যে আপনার পুনরাবৃত্তকারী বস্তুটি ভাগ করা উচিত নয়। একাধিক পুনরাবৃত্তকারী তৈরি করা এবং পৃথক থ্রেডে একযোগে ব্যবহার করা ভাল।


আপনি আইট্রেটারে আই ক্যাপিটাল করেননি এমন কোনও কারণ? যেহেতু এটি শ্রেণীর নাম, এটি কম বিভ্রান্ত হতে পারে।
বিল মিশেল

1
@ বিল মিশেল, এখন আমরা শিষ্টাচার পোস্ট করার শব্দার্থে রয়েছি। আমি মনে করি যে তাঁর উচিত ছিল একজন আইট্রেটারের জন্য জাভাডোকের কাছে ফিরে লিঙ্কটি তৈরি করা বা খুব কমপক্ষে এটিকে ইনলাইন কোড টীকা (।) এর ভিতরে রেখে দেওয়া উচিত।
টিম বেন্ডার

10

এটি আপনাকে একটি ভাল অন্তর্দৃষ্টি দিতে পারে

সমকালীন হ্যাশম্যাপ কলকারীদের প্রতিশ্রুতিগুলি সামান্যই শিথিল করে উচ্চতর সম্মতি অর্জন করে। একটি পুনরুদ্ধার অপারেশন সর্বাধিক সমাপ্ত sertোকানো অপারেশন দ্বারা সন্নিবেশিত মানটি ফিরিয়ে দেবে এবং একই সাথে অগ্রগতিতে থাকা সন্নিবেশ ক্রিয়াকলাপের সাথে যুক্ত একটি মানও ফিরিয়ে দিতে পারে (তবে কোনও ক্ষেত্রে এটি কোনও বাজে ফলাফল প্রদান করবে না)। কনকোর্নাল হ্যাশম্যাপ.আইটিরেটার () দ্বারা প্রতীয়মান ইটারেটর প্রতিটি উপাদান একবারে ফিরে আসবে এবং কখনই কনকেনারমোডিশনএক্সেপশন নিক্ষেপ করবে না, তবে পুনরুক্তি নির্মিত হওয়ার পরে সংযোজন বা অপসারণগুলি প্রতিফলিত করতে পারে বা নাও করতে পারে। সংগ্রহটি পুনরাবৃত্তি করার সময় থ্রেড-সুরক্ষা সরবরাহ করতে কোনও টেবিল-প্রশস্ত লকিং প্রয়োজন হয় না (বা এমনকি সম্ভব)। কনক্র্যান্টহ্যাশম্যাপটি কোনও অ্যাপ্লিকেশনে সিঙ্ক্রোনাইজড ম্যাপ বা হ্যাশটেবলের প্রতিস্থাপন হিসাবে ব্যবহার করা যেতে পারে যা আপডেটগুলি প্রতিরোধের জন্য পুরো টেবিলটিকে লক করার ক্ষমতার উপর নির্ভর করে না।

এই সংক্রান্ত:

যাইহোক, পুনরাবৃত্তিগুলি একবারে কেবল একটি থ্রেড দ্বারা ব্যবহার করার জন্য ডিজাইন করা হয়।

এর অর্থ হ'ল দুটি থ্রেডে কনকন্টার হ্যাশম্যাপ দ্বারা উত্পাদিত পুনরাবৃত্তিগুলি নিরাপদ, এটি প্রয়োগে অপ্রত্যাশিত ফলাফলের কারণ হতে পারে।


4

এর মানে কী?

এর অর্থ হল যে আপনি একই থ্রেটারে দুটি থ্রেডে ব্যবহার করার চেষ্টা করবেন না। আপনার যদি দুটি থ্রেড থাকে যা কীগুলি, মানগুলি বা এন্ট্রিগুলিতে পুনরাবৃত্তি হওয়া দরকার, তবে সেগুলির প্রতিটি তাদের নিজস্ব পুনরাবৃত্তকারী তৈরি এবং ব্যবহার করা উচিত।

যদি আমি একই সাথে দুটি থ্রেড সহ মানচিত্রটি পুনরাবৃত্তি করার চেষ্টা করি তবে কী হবে?

আপনি যদি এই নিয়মটি ভঙ্গ করেন তবে কী হবে তা পুরোপুরি পরিষ্কার নয়। আপনি কেবল বিভ্রান্তিকর আচরণ পেতে পারেন, আপনি একইভাবে করেন যদি (উদাহরণস্বরূপ) দুটি থ্রেড সিঙ্ক্রোনাইজ না করে স্ট্যান্ডার্ড ইনপুট থেকে পড়ার চেষ্টা করে। আপনি নন-থ্রেড-নিরাপদ আচরণও পেতে পারেন।

তবে যদি দুটি থ্রেড পৃথক পুনরুক্তি ব্যবহার করে তবে আপনার ভাল হওয়া উচিত।

আমি মানচিত্রটি পুনরাবৃত্ত করার সময় মানটি রেখে দিলে বা সরিয়ে ফেললে কী হবে?

এটি একটি পৃথক সমস্যা, তবে যে জাভাদোক অংশটি আপনি উদ্ধৃত করেছেন তা এর যথাযথভাবে উত্তর দেয়। মূলত, পুনরুক্তিগুলি থ্রেড-নিরাপদ, তবে আপনি পুনরুক্তিকারীর দ্বারা প্রত্যাবর্তিত বস্তুর ক্রমে কোনও সমবর্তী সন্নিবেশ, আপডেট বা মুছে ফেলার প্রভাব দেখতে পাবেন কিনা তা সংজ্ঞায়িত হয় না । অনুশীলনে, এটি সম্ভবত মানচিত্রের আপডেটগুলি কোথায় ঘটে তার উপর নির্ভর করে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.