কেন একটি সমকালীন মডিফিকেশন ধারণাটি নিক্ষেপ করা হয় এবং কীভাবে এটি ডিবাগ করা যায়


130

আমি Collectionএকটি HashMapব্যবহার করছি ( জেপিএ দ্বারা পরোক্ষভাবে ব্যবহৃত, এটি এরকম ঘটে), তবে দৃশ্যত এলোমেলোভাবে কোডটি একটি ছুঁড়েছে ConcurrentModificationException। কী কারণে এটি ঘটছে এবং আমি কীভাবে এই সমস্যাটি সমাধান করব? কিছু সিঙ্ক্রোনাইজেশন ব্যবহার করে, সম্ভবত?

এখানে পুরো স্ট্যাক-ট্রেস রয়েছে:

Exception in thread "pool-1-thread-1" java.util.ConcurrentModificationException
        at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
        at java.util.HashMap$ValueIterator.next(Unknown Source)
        at org.hibernate.collection.AbstractPersistentCollection$IteratorProxy.next(AbstractPersistentCollection.java:555)
        at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
        at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
        at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
        at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
        at org.hibernate.engine.Cascade.cascade(Cascade.java:130)

1
আপনি কি আরও কিছু প্রসঙ্গ সরবরাহ করতে পারেন? আপনি কি কোনও সত্ত্বাকে মার্জ করছেন, আপডেট করছেন বা মুছে ফেলছেন? এই সত্তার কোন সমিতি রয়েছে? আপনার ক্যাসকেডিং সেটিংস সম্পর্কে কী?
অর্ডনঙ্গসভিডরিগ

1
স্ট্যাক ট্রেস থেকে আপনি দেখতে পাচ্ছেন যে হ্যাশম্যাপের মাধ্যমে পুনরাবৃত্তি করার সময় ব্যতিক্রম ঘটে। অবশ্যই অন্য কিছু থ্রেড মানচিত্রটি পরিবর্তন করছে তবে ব্যতিক্রম ঘটানো থ্রেডে ব্যতিক্রম ঘটে।
চকোস

উত্তর:


263

এটি একটি সিঙ্ক্রোনাইজেশন সমস্যা নয়। এটি ঘটবে যদি অন্তর্নিহিত সংগ্রহটি পুনরাবৃত্তি হচ্ছে

Iterator it = map.entrySet().iterator();
while (it.hasNext())
{
   Entry item = it.next();
   map.remove(item.getKey());
}

এটি দ্বিতীয়বার ConcurrentModificationExceptionযখন it.hasNext()ডাকা হবে তখন এটি ফেলে দেবে ।

সঠিক পন্থা হবে

   Iterator it = map.entrySet().iterator();
   while (it.hasNext())
   {
      Entry item = it.next();
      it.remove();
   }

এই পুনরুক্তিকারীকে ধরে নেওয়া remove()অপারেশনটিকে সমর্থন করে ।


1
সম্ভবত, তবে দেখে মনে হচ্ছে হাইবারনেট পুনরাবৃত্তি করছে যা যুক্তিসঙ্গতভাবে সঠিকভাবে প্রয়োগ করা উচিত। মানচিত্র সংশোধনকারী কলব্যাক থাকতে পারে, তবে এর সম্ভাবনা নেই। অবিশ্বাস্যতা একটি প্রকৃত সম্মতি সমস্যার দিকে নির্দেশ করে।
টম হাটিন -

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

তবে আমি সম্মত হই যে এটি যদি অনির্দেশ্য হয় তবে খুব সম্ভবত থ্রেডিংয়ের সমস্যা রয়েছে যা এই ব্যতিক্রমের শর্তগুলি ঘটায়। যা ব্যতিক্রম নামের কারণে এটি আরও বিভ্রান্তিকর করে তোলে।
রবিন

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

এই সমাধানটির কোনও অর্থ নেই, কারণ মানচিত্রে পুনরাবৃত্তিকারী () পদ্ধতি নেই। রবিনের উদাহরণ যেমন তালিকাগুলির ক্ষেত্রে প্রযোজ্য।
পিটার

72

ConcurrentHashMapএকটি সমতল পরিবর্তে একটি ব্যবহার করার চেষ্টা করুনHashMap


সত্যিই কি সমস্যার সমাধান হয়েছে? আমি একই সমস্যাটি অনুভব করছি তবে আমি অবশ্যই কোনও থ্রেডিংয়ের বিষয়টি বাতিল করতে পারি।
tobiasbayer

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

হাইবারনেট যিনি সংগ্রহের মাধ্যমে পুনরাবৃত্তি করছেন যাতে আপনি এটি অনুলিপি করতে পারবেন না।
tobiasbayer

1
তাত্ক্ষণিক ত্রাণকর্তা। কেন এটি এত ভাল কাজ করেছে তা খতিয়ে দেখার জন্য আমি আরও রাস্তায় আরও বিস্মিত হই না।
ভালচারিস

1
আমার ধারণা এটির সিঙ্ক্রোনাইজেশন ইস্যুটি নয় একই সমস্যাটি লুপিংয়ের সময় একই পরিবর্তন সংশোধন করা সমস্যা is
রইস আলম

17

একটি সংশোধন Collectionযখন iterating মাধ্যমে Collectionব্যবহার করে একটি Iteratorহয় অনুমতি নেই অধিকাংশ দ্বারা Collectionক্লাস। জাভা লাইব্রেরি Collectionএটির সাথে "একযোগে পরিবর্তন" পুনরুক্তি করার সময় কিছু সংশোধন করার প্রচেষ্টা বলে। দুর্ভাগ্যক্রমে একমাত্র সম্ভাব্য কারণটি একাধিক থ্রেড দ্বারা একযোগে সংশোধন করার পরামর্শ দেয়, তবে এটি তেমন নয়। শুধুমাত্র একটি থ্রেড ব্যবহার করে Collection(ব্যবহার করে Collection.iterator()বা বর্ধিত forলুপের জন্য ) একটি পুনরুক্তি তৈরি করা , পুনরাবৃত্তি শুরু করা (ব্যবহার করা Iterator.next()বা সমতুল্য বর্ধিত forলুপের শরীরে প্রবেশ করা ), সংশোধন করুন Collection, তারপরে পুনরাবৃত্তি চালিয়ে যাওয়া সম্ভব।

প্রোগ্রামারদের সহায়তা করতে , এই শ্রেণীর কিছু বাস্তবায়ন ভুল যুগ্ম পরিবর্তনটি সনাক্ত করার চেষ্টা করে এবং যদি এটি সনাক্ত করে তবে একটি নিক্ষেপ করে। যাইহোক, সমস্ত সমবর্তী পরিবর্তনগুলির সনাক্তকরণের গ্যারান্টি দেওয়া সাধারণভাবে সম্ভব এবং ব্যবহারিক নয়। সুতরাং এর ভ্রান্ত ব্যবহার সর্বদা নিক্ষেপ হয় না ।CollectionConcurrentModificationExceptionCollectionConcurrentModificationException

এর ডকুমেন্টেশন ConcurrentModificationExceptionবলেছেন:

এই ব্যতিক্রমটি এমন পদ্ধতিগুলির দ্বারা ছুঁড়ে দেওয়া যেতে পারে যেগুলি যখন এই জাতীয় পরিবর্তন অনুমোদিত না হয় তখন কোনও সামগ্রীর একযোগে সংশোধন করে ...

নোট করুন যে এই ব্যতিক্রমটি সর্বদা ইঙ্গিত দেয় না যে কোনও বস্তু একই সময়ে ভিন্ন থ্রেড দ্বারা সংশোধন করা হয়েছে। যদি কোনও এক থ্রেড কোনও পদ্ধতির অনুরোধগুলির ক্রম জারি করে যা কোনও বস্তুর চুক্তি লঙ্ঘন করে তবে অবজেক্টটি এই ব্যতিক্রমটিকে ছুঁড়ে ফেলতে পারে ...

নোট করুন যে ব্যর্থ-দ্রুত আচরণের গ্যারান্টি দেওয়া যায় না, যেমনটি সাধারণভাবে বলা যায়, অযৌক্তিক সমান্তরাল পরিবর্তনের উপস্থিতিতে কোনও কঠোর গ্যারান্টি দেওয়া অসম্ভব। ব্যর্থ-দ্রুত ক্রিয়াকলাপগুলি ConcurrentModificationExceptionসেরা-প্রচেষ্টা ভিত্তিতে ফেলে দেয়।

মনে রাখবেন যে

ডকুমেন্টেশন HashSet, HashMap, TreeSetএবং ArrayListক্লাস এই বলে:

পুনরুদ্ধারকারীরা [প্রত্যক্ষ বা অপ্রত্যক্ষভাবে এই শ্রেণি থেকে] ব্যর্থ দ্রুত: পুনরুক্তিটি তৈরির পরে [সংগ্রহ] যদি যে কোনও সময়ে সংশোধন করা হয় তবে পুনরাবৃত্তির নিজস্ব অপসারণ পদ্ধতি বাদ দিয়ে কোনওভাবেই Iteratorছুড়ে ফেলা হয় ConcurrentModificationException। সুতরাং, একযোগে পরিবর্তনের মুখে, পুনরুক্তিকারী ভবিষ্যতে একটি নির্ধারিত সময়ে স্বেচ্ছাসেবক, অ-সংজ্ঞাবহ আচরণের ঝুঁকির চেয়ে দ্রুত এবং পরিষ্কার ব্যর্থ হয়।

নোট করুন যে কোনও পুনরাবৃত্তকারীটির ব্যর্থ-দ্রুত আচরণের গ্যারান্টি দেওয়া যায় না যেমন এটি সাধারণভাবে বলা যায়, অযত্মাত্ত্বিক সমবর্তী পরিবর্তনের উপস্থিতিতে কোনও কঠোর গ্যারান্টি দেওয়া অসম্ভব। ব্যর্থ-দ্রুত পুনরাবৃত্তি ConcurrentModificationExceptionএকটি সেরা প্রচেষ্টা ভিত্তিতে নিক্ষেপ । অতএব, কোনও প্রোগ্রাম লিখতে ভুল হবে যা এর যথাযথতার জন্য এই ব্যতিক্রমটির উপর নির্ভর করে: পুনরাবৃত্তিকারীদের ব্যর্থ-দ্রুত আচরণ কেবল বাগগুলি সনাক্ত করতে ব্যবহার করা উচিত

আবার নোট করুন যে আচরণটির "গ্যারান্টি দেওয়া যায় না" এবং কেবল "সেরা-প্রচেষ্টা ভিত্তিতে"।

Mapইন্টারফেসের বিভিন্ন পদ্ধতির ডকুমেন্টেশন এটিকে বলে:

অবিচ্ছিন্ন বাস্তবায়নগুলি এই পদ্ধতিটিকে ওভাররাইড করা উচিত এবং সর্বোত্তম চেষ্টাের ভিত্তিতে ConcurrentModificationExceptionযদি এটি সনাক্ত হয় যে ম্যাপিং ফাংশনটি গণনার সময় এই মানচিত্রটি পরিবর্তন করে। একযোগে বাস্তবায়নগুলি এই পদ্ধতিটিকে ওভাররাইড করে এবং সর্বোত্তম প্রচেষ্টার ভিত্তিতে, IllegalStateExceptionযদি এটি সনাক্ত হয় যে ম্যাপিং ফাংশনটি গণনার সময় এই মানচিত্রটি পরিবর্তন করে এবং ফলস্বরূপ গণনা কখনই সম্পূর্ণ হয় না।

আবার মনে রাখবেন যে সনাক্তকরণের জন্য কেবলমাত্র "সেরা-প্রচেষ্টা ভিত্তি" প্রয়োজন, এবং ConcurrentModificationExceptionএগুলি কেবলমাত্র অবিকৃত (নন থ্রেড-নিরাপদ) শ্রেণীর জন্য সুস্পষ্টভাবে প্রস্তাবিত।

ডিবাগ ConcurrentModificationException

সুতরাং, যখন আপনি কোনও কারণে স্ট্যাক-ট্রেস দেখেন ConcurrentModificationException, আপনি তাত্ক্ষণিকভাবে ধরে নিতে পারবেন না যে কারণটি একটিতে অরক্ষিত বহু-থ্রেড অ্যাক্সেস Collection। কোন শ্রেণীর ব্যতিক্রম ছুঁড়েছে তা নির্ধারণ করতে আপনাকে অবশ্যই স্ট্যাক-ট্রেস পরীক্ষা করতে হবে Collection(শ্রেণীর কোনও পদ্ধতি প্রত্যক্ষ বা অপ্রত্যক্ষভাবে এটিকে নিক্ষেপ করবে) এবং কোন Collectionবস্তুর জন্য। তারপরে আপনাকে অবশ্যই পরীক্ষা করতে হবে যেখান থেকে সেই বস্তুটি সংশোধন করা যেতে পারে।

  • সর্বাধিক সাধারণ কারণটি হল Collectionএকটি বর্ধিত forলুপের মধ্যে পরিবর্তনটি Collection। আপনি কেবল Iteratorআপনার উত্স কোডে কোনও অবজেক্ট দেখতে পাচ্ছেন না তার অর্থ এই নয় যে সেখানে কোনও Iteratorনেই! ভাগ্যক্রমে, ত্রুটিযুক্ত forলুপের একটি বিবৃতি সাধারণত স্ট্যাক-ট্রেসে থাকবে, সুতরাং ত্রুটিটি সনাক্ত করা সাধারণত সহজ is
  • একটি ট্রিকায়ার কেস তখন হয় যখন আপনার কোডটি Collectionঅবজেক্টের রেফারেন্সের চারপাশে পাস করে । দ্রষ্টব্য যে সংগ্রহগুলির অবিশ্বাস্য দৃশ্য (যেমন উত্পাদিত Collections.unmodifiableList()) সংশোধনযোগ্য সংগ্রহের একটি রেফারেন্স ধরে রাখে, সুতরাং "অপরিবর্তনীয়" সংগ্রহের উপর পুনরাবৃত্তি ব্যতিক্রমটিকে ছুঁড়ে ফেলতে পারে (পরিবর্তন অন্যত্র করা হয়েছে)। অন্যান্য মতামত আপনার এর Collectionযেমন সাব তালিকা , Mapপ্রবেশ সেট এবং Mapকী সংকলন এছাড়াও মূল (সংশোধনযোগ্য) এর রেফারেন্স ধরে রাখা Collection। এমনকি একটি থ্রেড-নিরাপদ জন্য একটি সমস্যা হতে পারে Collection, যেমন CopyOnWriteList; অনুমান করবেন না যে থ্রেড-নিরাপদ (সমবর্তী) সংগ্রহগুলি কখনই ব্যতিক্রম ছুঁড়ে ফেলতে পারে না।
  • কোন ক্রিয়াকলাপ কোনওটিকে সংশোধন Collectionকরতে পারে কিছু ক্ষেত্রে অপ্রত্যাশিত হতে পারে। উদাহরণস্বরূপ, LinkedHashMap.get()এর সংগ্রহটি পরিবর্তন করে
  • কঠিন মামলা যখন ব্যতিক্রম হয় একাধিক থ্রেড দ্বারা সমবর্তী পরিমার্জন কারণে।

একযোগে পরিবর্তন ত্রুটি রোধ করতে প্রোগ্রামিং

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


আমি আশ্চর্য হই যে কেন একক থ্রেডের ক্ষেত্রে একযোগে পরিবর্তনের অনুমতি দেওয়া হচ্ছে না। নিয়মিত হ্যাশ মানচিত্রে কোনও একক থ্রেডকে একযোগে পরিবর্তন করার অনুমতি দেওয়া হলে কী সমস্যা দেখা দিতে পারে?
জো

4

জাভা 8-এ, আপনি ল্যাম্বদা এক্সপ্রেশন ব্যবহার করতে পারেন:

map.keySet().removeIf(key -> key condition);

2

এটি জাভা সিঙ্ক্রোনাইজেশন ইস্যুর মতো কম এবং আরও ডেটাবেস লকিং সমস্যার মতো শোনাচ্ছে।

আপনার সমস্ত অবিচলিত ক্লাসে কোনও সংস্করণ যুক্ত করা গেলে তা সাজানো হবে কিনা তা আমি জানি না, তবে এটি হায়বারনেট কোনও টেবিলের সারিগুলিতে একচেটিয়া অ্যাক্সেস সরবরাহ করতে পারে way

এমন কি হতে পারে যে বিচ্ছিন্নতা স্তরটি আরও বেশি হওয়া দরকার। যদি আপনি "নোংরা পাঠ্য" মঞ্জুর করেন, সম্ভবত আপনাকে সিরিয়ালিয়াল করার জন্য ধাক্কা খাওয়ার দরকার আছে।


আমি মনে করি তারা হ্যাশটেবলকে বোঝায়। এটি JDK 1.0 এর অংশ হিসাবে প্রেরণ করা হয়েছে। ভেক্টরের মতো, এটি থ্রেড নিরাপদ - এবং ধীর হতে লেখা হয়েছিল। উভয়কেই নন-থ্রেড নিরাপদ বিকল্পগুলি দ্বারা বরখাস্ত করা হয়েছে: হ্যাশম্যাপ এবং অ্যারেলিস্ট। আপনি যা ব্যবহার করেন তার জন্য অর্থ প্রদান করুন।
duffymo

0

আপনি যা করার চেষ্টা করছেন তার উপর নির্ভর করে কপিরাইটঅনরাইটআরলিলিস্ট বা কপিরাইটঅনরাইটআর্রেসিট চেষ্টা করুন।


0

মনে রাখবেন যে নির্বাচিত উত্তরটি সরাসরি আপনার প্রসঙ্গে কিছু সংশোধন করার আগে প্রয়োগ করা যাবে না, যদি আপনি ঠিক আমার মতো মানচিত্রের পুনরাবৃত্তি করার সময় মানচিত্র থেকে কিছু প্রবেশিকা সরানোর চেষ্টা করছেন।

আমি এখানে নতুন কাজের জন্য তাদের সময় বাঁচানোর জন্য আমার কাজের উদাহরণটি দিচ্ছি:

HashMap<Character,Integer> map=new HashMap();
//adding some entries to the map
...
int threshold;
//initialize the threshold
...
Iterator it=map.entrySet().iterator();
while(it.hasNext()){
    Map.Entry<Character,Integer> item=(Map.Entry<Character,Integer>)it.next();
    //it.remove() will delete the item from the map
    if((Integer)item.getValue()<threshold){
        it.remove();
    }

0

তালিকা থেকে এক্স টি শেষ আইটেম সরিয়ে দেওয়ার চেষ্টা করার সময় আমি এই ব্যতিক্রম ঘটলাম ran myList.subList(lastIndex, myList.size()).clear();আমার জন্য কাজ করা একমাত্র সমাধান ছিল।

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