concurrency - أنواع مختلفة من مجموعات الخيط الآمن في Java




set (4)

1) يعد CopyOnWriteArraySet بسيطًا للغاية - فهو يحتوي أساسًا على قائمة بالعناصر في صفيف ، وعند تغيير القائمة ، فإنه ينسخ المصفوفة. تستمر التكرارات والقيود الأخرى التي تعمل في هذا الوقت مع الصف القديم ، مع تجنب ضرورة التزامن بين القراء والكتاب (على الرغم من أن الكتابة نفسها تحتاج إلى التزامن). عادةً ما تكون عمليات المجموعة السريعة عادة (خاصة contains() ) بطيئة جدًا ، حيث سيتم البحث في المصفوفات في وقت خطي.

استخدم هذا فقط للمجموعات الصغيرة التي ستتم قراءتها (تكررت) وغالبًا ما يتم تغييرها. (قد تكون مجموعات المستمعين المتأرجحة مثالاً ، لكن هذه ليست مجموعات بالفعل ، ويجب استخدامها فقط من EDT على أي حال).

2) ستقوم Collections.synchronizedSet بكل بساطة بلف كتلة متزامنة حول كل طريقة من المجموعة الأصلية. يجب عدم الوصول إلى المجموعة الأصلية مباشرة. هذا يعني أنه لا يمكن تنفيذ طريقتين من المجموعة بشكل متزامن (سيتم حظر أحدهما حتى ينتهي الآخر) - وهذا آمن من مؤشر الترابط ، ولكن لن يكون لديك التزامن إذا كانت مؤشرات الترابط المتعددة تستخدم بالفعل المجموعة. إذا كنت تستخدم المكرر ، فعادة ما تكون في حاجة إلى التزامن خارجيًا لتجنب ConcurrentModificationExceptions عند تعديل المجموعة بين مكالمات الحاوية. سيكون الأداء مثل أداء المجموعة الأصلية (ولكن مع بعض الحمل الزائد ، والحظر إذا تم استخدامه بشكل متزامن).

استخدم هذا إذا كان لديك التزامن منخفض فقط ، وتريد أن تتأكد من أن كافة التغييرات مرئية على الفور لمؤشرات الترابط الأخرى.

3) ConcurrentSkipListSet هو تنفيذ SortedSet المتزامن ، مع معظم العمليات الأساسية في O (سجل n). وهو يسمح بالإضافة / الإزالة المتزامنة والقراءة / التكرار ، حيث قد يفسح التكرار أو لا يخبر بالتغييرات منذ إنشاء المكرر. العمليات المجمعة هي ببساطة مكالمات فردية متعددة ، وليس بشكل متسلسل - قد تلاحظ سلاسل أخرى فقط بعض منها فقط.

من الواضح أنه لا يمكنك استخدام هذا إلا إذا كان لديك بعض الطلب الإجمالي على العناصر الخاصة بك. يبدو هذا مثل مرشح مثالي لحالات التزامن عالية ، للمجموعات غير كبيرة جداً (بسبب O (log n)).

4) بالنسبة لـ ConcurrentHashMap (والمجموعة المشتقة منه): هنا معظم الخيارات الأساسية (في المتوسط ​​، إذا كان لديك hashCode() جيد وسريع في O (1) (ولكن قد يتحول إلى O (n)) ، مثل ل HashMap / HashSet. يوجد التزامن محدود للكتابة (يتم تقسيم الجدول ، وستتم مزامنة الوصول للكتابة على القسم المطلوب) ، بينما يكون الوصول للقراءة متزامناً تمامًا مع نفسه وموضوعات الكتابة (ولكن قد لا يرى حتى الآن نتائج التغييرات التي يتم إجراؤها حاليًا). مكتوب). قد يلاحظ أو لا يرى المكرر التغييرات منذ إنشائه ، والعمليات المجمعة ليست ذرية. تغيير الحجم بطيء (مثل HashMap / HashSet) ، وبالتالي حاول تجنب هذا عن طريق تقدير الحجم المطلوب عند الإنشاء (واستخدام حوالي 1/3 أكثر من ذلك ، حيث يتم تغيير حجم عند 3/4 كامل).

استخدم هذا عندما يكون لديك مجموعات كبيرة ، وظيفة تجزئة جيدة (وسريعة) ويمكن تقدير حجم المجموعة والتزامن المطلوب قبل إنشاء الخريطة.

5) هل هناك تطبيقات خريطة متزامنة أخرى يمكن للمرء استخدامها هنا؟

يبدو أن هناك الكثير من التطبيقات والطرق المختلفة لإنشاء مجموعات خيط آمن في Java. بعض الأمثلة تشمل

1) CopyOnWriteArraySet

2) Collections.synchronizedSet (مجموعة)

3) ConcurrentSkipListSet

4) Collections.newSetFromMap (جديد ConcurrentHashMap ())

5) مجموعات أخرى تم إنشاؤها بطريقة مشابهة لـ (4)

تأتي هذه الأمثلة من نمط التزامن: مجموعة متزامنة في تطبيقات Java 6

هل يمكن لشخص ما أن يشرح ببساطة الاختلافات والمزايا وعيوب هذه الأمثلة وغيرها؟ أواجه مشكلة في فهم كل شيء ومستنداته من Java Std Docs.


من الممكن دمج الأداء contains() من HashSet مع الخصائص المرتبطة بالتزامن لـ CopyOnWriteArraySet باستخدام AtomicReference<Set> واستبدال المجموعة بأكملها في كل تعديل.

رسم التنفيذ:

public abstract class CopyOnWriteSet<E> implements Set<E> {

    private final AtomicReference<Set<E>> ref;

    protected CopyOnWriteSet( Collection<? extends E> c ) {
        ref = new AtomicReference<Set<E>>( new HashSet<E>( c ) );
    }

    @Override
    public boolean contains( Object o ) {
        return ref.get().contains( o );
    }

    @Override
    public boolean add( E e ) {
        while ( true ) {
            Set<E> current = ref.get();
            if ( current.contains( e ) ) {
                return false;
            }
            Set<E> modified = new HashSet<E>( current );
            modified.add( e );
            if ( ref.compareAndSet( current, modified ) ) {
                return true;
            }
        }
    }

    @Override
    public boolean remove( Object o ) {
        while ( true ) {
            Set<E> current = ref.get();
            if ( !current.contains( o ) ) {
                return false;
            }
            Set<E> modified = new HashSet<E>( current );
            modified.remove( o );
            if ( ref.compareAndSet( current, modified ) ) {
                return true;
            }
        }
    }

}

إذا لم يساعد Javadocs ، فمن المحتمل أن تجد كتابًا أو مقالة للقراءة عن هياكل البيانات. في لمحة:

  • يجعل CopyOnWriteArraySet نسخة جديدة من الصفيف الأساسي في كل مرة تقوم فيها بتحويل المجموعة ، لذلك تكون الكتابة بطيئة وتكون Iterators سريعة ومتسقة.
  • يستخدم Collections.synchronizedSet () استدعاءات الأسلوب القديم - المزامنة لإجراء تعيين threadsafe. سيكون هذا نسخة منخفضة الأداء.
  • يوفر ConcurrentSkipListSet يكتب الأداء مع عمليات دفعة غير متناسقة (addAll ، removeAll ، الخ) و Iterators.
  • Collections.newSetFromMap (جديد ConcurrentHashMap ()) يحتوي على دلالات ConcurrentHashMap ، والتي أعتقد أنها ليست بالضرورة محسنة للقراءة أو الكتابة ، ولكن مثل ConcurrentSkipListSet ، لديها عمليات دفعية غير متناسقة.

لدي حل ، لكني لا أعرف مدى فعالية ذلك. لكنها تعمل بشكل جيد ، وأعتقد أنه يمكنك تحسينها. من ناحية أخرى ، قمت JUnit مع JUnit وهي خطوة بشكل صحيح. أرفقت الوظيفة والاختبار:

static public Integer str2Int(String str) {
    Integer result = null;
    if (null == str || 0 == str.length()) {
        return null;
    }
    try {
        result = Integer.parseInt(str);
    } 
    catch (NumberFormatException e) {
        String negativeMode = "";
        if(str.indexOf('-') != -1)
            negativeMode = "-";
        str = str.replaceAll("-", "" );
        if (str.indexOf('.') != -1) {
            str = str.substring(0, str.indexOf('.'));
            if (str.length() == 0) {
                return (Integer)0;
            }
        }
        String strNum = str.replaceAll("[^\\d]", "" );
        if (0 == strNum.length()) {
            return null;
        }
        result = Integer.parseInt(negativeMode + strNum);
    }
    return result;
}

اختبار مع JUnit:

@Test
public void testStr2Int() {
    assertEquals("is numeric", (Integer)(-5), Helper.str2Int("-5"));
    assertEquals("is numeric", (Integer)50, Helper.str2Int("50.00"));
    assertEquals("is numeric", (Integer)20, Helper.str2Int("$ 20.90"));
    assertEquals("is numeric", (Integer)5, Helper.str2Int(" 5.321"));
    assertEquals("is numeric", (Integer)1000, Helper.str2Int("1,000.50"));
    assertEquals("is numeric", (Integer)0, Helper.str2Int("0.50"));
    assertEquals("is numeric", (Integer)0, Helper.str2Int(".50"));
    assertEquals("is numeric", (Integer)0, Helper.str2Int("-.10"));
    assertEquals("is numeric", (Integer)Integer.MAX_VALUE, Helper.str2Int(""+Integer.MAX_VALUE));
    assertEquals("is numeric", (Integer)Integer.MIN_VALUE, Helper.str2Int(""+Integer.MIN_VALUE));
    assertEquals("Not
     is numeric", null, Helper.str2Int("czv.,xcvsa"));
    /**
     * Dynamic test
     */
    for(Integer num = 0; num < 1000; num++) {
        for(int spaces = 1; spaces < 6; spaces++) {
            String numStr = String.format("%0"+spaces+"d", num);
            Integer numNeg = num * -1;
            assertEquals(numStr + ": is numeric", num, Helper.str2Int(numStr));
            assertEquals(numNeg + ": is numeric", numNeg, Helper.str2Int("- " + numStr));
        }
    }
}




java concurrency set