2012-12-22 25 views
12

हमारे बहुत सारे कोड विरासत हैं लेकिन हम "बिग डेटा" बैक एंड के लिए आगे बढ़ रहे हैं और मैं नवीनतम एपीआई कॉल का प्रचार करने की कोशिश कर रहा हूं, नवीनतम स्प्रिंग लाइब्रेरी आदि के उपयोग को प्रोत्साहित करता हूं। हमारी समस्याओं में से एक आवेदन है परत आईडी पीढ़ी। कारणों से मुझे समझ में नहीं आता है, एक उच्च प्राधिकरण अनुक्रमिक BigInteger चाहता है। मैं उन्हें फिर से उत्पन्न करने के साथ यादृच्छिक बना देता और विफल प्रविष्टियों पर पुनः प्रयास करता था लेकिन मुझे vetoed मिल गया।एक थ्रेड सुरक्षित तरीके से BigInteger को सुरक्षित रूप से बढ़ाना संभव है, शायद परमाणु संदर्भ के साथ, w/o लॉकिंग?

एक तरफ झुकाव, मैं ऐसी स्थिति में हूं जहां मुझे बढ़ने और थ्रेड में बिगइंटर प्राप्त करने की आवश्यकता है और इसे एक सुरक्षित और निष्पादक तरीके से करें। मैंने पहले कभी परमाणु संदर्भ का उपयोग नहीं किया है, लेकिन यह इस एप्लिकेशन के लिए बिल्कुल सही दिखता है। अभी हमारे पास एक सिंक्रनाइज़ कोड ब्लॉक है जो हमारे प्रदर्शन को बहुत बुरी तरह खराब करता है।

क्या यह सही तरीका है? सिंटेक्स उदाहरण?

मुझे यह उल्लेख करना चाहिए कि इस मॉड्यूल के तरीके के तरीके से, यह उपयोग करने के लिए मूल्यों की एक श्रृंखला को पकड़ने के लिए संग्रहीत प्रक्रिया का उपयोग कर डेटाबेस को हिट करता है। एक समय में हजारों लोग ताकि यह केवल 20 मिनट में हो सकता है। यह विभिन्न सर्वरों को एक-दूसरे पर कदम उठाने से रोकता है लेकिन यह बिगइंटर को मनमाने ढंग से बाद के मूल्य पर सेट करने की झुर्रियां भी जोड़ता है। बेशक, इसे थ्रेड सुरक्षित भी होना चाहिए।

पीएस मुझे अभी भी लगता है कि मेरी यादृच्छिक पीढ़ी का विचार यह सब थ्रेडिंग सामानों को संभालने से बेहतर है। एक बिगइंटर एक हास्यास्पद रूप से बड़ी संख्या है और कभी भी दो बार उत्पन्न करने की बाधाओं को शून्य के करीब होना चाहिए।

public final class AtomicBigInteger { 

    private final AtomicReference<BigInteger> valueHolder = new AtomicReference<>(); 

    public AtomicBigInteger(BigInteger bigInteger) { 
     valueHolder.set(bigInteger); 
    } 

    public BigInteger incrementAndGet() { 
     for (; ;) { 
      BigInteger current = valueHolder.get(); 
      BigInteger next = current.add(BigInteger.ONE); 
      if (valueHolder.compareAndSet(current, next)) { 
       return next; 
      } 
     } 
    } 
} 

यह मूल रूप से के लिए incrementAndGet()

+2

'परमाणु संदर्भ' जैसा प्रतीत नहीं होता है यह आपके लिए लॉक किए बिना करता है। लेकिन जब तक आपको लंबे समय से अधिक पूर्णांक की आवश्यकता नहीं होती है, तो मुझे आश्चर्य होगा कि अगर बिगइंटरर आपको धीमा नहीं कर रहा था। 'परमाणु लोंग' के साथ जाने के लिए उन्हें मनाने की कोशिश करें। –

+0

मैं उसमें देख लूंगा। भले ही, मैं अभी भी उत्तर के बारे में उत्सुकता से उत्सुक हूं। ;-) विशेष रूप से क्योंकि वे मुझे फिर से veto सकता है। राजनीति। – user447607

+0

परमाणु संदर्भ (जैसे अन्य परमाणु .. वर्ग) लॉकिंग का उपयोग नहीं करते हैं। – bowmore

उत्तर

12

यह यहाँ AtomicReference का उपयोग कर के लिए संभव है एक त्वरित मसौदा है।

व्यक्तिगत तौर पर मैं इस कोड के रूप में सैद्धांतिक रूप से यह भुखमरी सूत्र में बाँधना (हालांकि जिस व्यक्ति ने यह मेरे लिए दिखाया यह कभी नहीं दावा है होता है)

private AtomicReference<BigInteger> ref = new AtomicReference<BigInteger>(BigInteger.ZERO); 

public BigInteger incrementAndGet() { 
    BigInteger currVal, newVal; 
    do { 
     currVal = ref.get(); 
     newVal = currVal.clone(); 
     newVal.add(BigInteger.ONE); 
    } while (!ref.compareAndSet(currVal, newVal)); 

} 

मैं यदि संभव हो तो AtomicLong साथ जाना होगा ले जा सकता है पसंद नहीं है।

+0

मेरा जवाब लिखते समय यह जवाब नहीं देखा :) –

+0

मुझे हर समय होता है :) – bowmore

+0

हाँ, मैं बस उस कोड को देख रहा था, यहां: http://www.docjar.com/html/ एपीआई/जावा/उपयोग/समवर्ती/परमाणु/AtomicLong.java.html – user447607

0

यह मूलतः अधिक से अधिक कोशिश करता है जब तक आपरेशन परमाणु था AtomicLong कोड की एक प्रति है:

+0

'जावा कंसुरेंसी इन प्रैक्टिस' से: "सैद्धांतिक रूप से, अगर अन्य धागे सीएएस दौड़ जीतते रहें तो इसे मनमाने ढंग से कई बार कोशिश करनी पड़ सकती है; व्यावहारिक रूप से, इस प्रकार का भूख शायद ही कभी होता है।" – bowmore

+1

हाँ मुझे पता है, लेकिन यह गलत लगता है :) –

+0

@AviramSegal: "क्लोन" में थोड़ा टाइपो। भुखमरी के संबंध में, यह संभव है कि कम प्राथमिकता वाले धागे को कई उच्च प्राथमिकता वाले धागे के खिलाफ प्रतिस्पर्धा करनी पड़े, लेकिन कुछ बैकऑफ-एंड-रीट्री एल्गोरिदम के विपरीत, यह गारंटी देता है कि कम से कम * कोई * आगे बढ़ेगा। – supercat

3

यह या तो रिटर्न अधिक प्रबंधनीय और accumulateAndGet या getAndAccumulate जावा 8. में शुरू की गई ये आप atomically एक संचायक समारोह है कि फ़ंक्शन के परिणाम पर मूल्य सेट की आपूर्ति करके मान को अद्यतन करने की अनुमति का उपयोग कर समझने के लिए आसान हो जाता है, और भी आपको जो चाहिए उसे निर्भर करते हुए पिछले या गणना परिणाम।

import java.math.BigInteger; 
import java.util.Objects; 
import java.util.concurrent.atomic.AtomicReference; 

public final class AtomicBigInteger { 

    private final AtomicReference<BigInteger> bigInteger; 

    public AtomicBigInteger(final BigInteger bigInteger) { 
    this.bigInteger = new AtomicReference<>(Objects.requireNonNull(bigInteger)); 
    } 

    // Method references left out for demonstration purposes 
    public BigInteger incrementAndGet() { 
    return bigInteger.accumulateAndGet(BigInteger.ONE, (previous, x) -> previous.add(x)); 
    } 

    public BigInteger getAndIncrement() { 
    return bigInteger.getAndAccumulate(BigInteger.ONE, (previous, x) -> previous.add(x)); 
    } 

    public BigInteger get() { 
    return bigInteger.get(); 
    } 
} 

एक उदाहरण इसे प्रयोग:

import java.math.BigInteger; 
import java.util.List; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 
import java.util.concurrent.TimeUnit; 
import java.util.stream.Collectors; 
import java.util.stream.IntStream; 

public class ABIExample { 

    private static final int AVAILABLE_PROCS = Runtime.getRuntime().availableProcessors(); 
    private static final int INCREMENT_AMOUNT = 2_500_000; 
    private static final int TASK_AMOUNT = AVAILABLE_PROCS * 2; 
    private static final BigInteger EXPECTED_VALUE = BigInteger.valueOf(INCREMENT_AMOUNT) 
                  .multiply(BigInteger 
                      .valueOf(TASK_AMOUNT)); 

    public static void main(String[] args) 
     throws InterruptedException, ExecutionException { 
    System.out.println("Available processors: " + AVAILABLE_PROCS); 


    final ExecutorService executorService = Executors 
     .newFixedThreadPool(Runtime.getRuntime().availableProcessors()); 

    final AtomicBigInteger atomicBigInteger = new AtomicBigInteger(BigInteger.ZERO); 

    final List<Callable<Void>> incrementTasks = IntStream.rangeClosed(1, TASK_AMOUNT) 
      .mapToObj(i -> incrementTask(i, atomicBigInteger)) 
      .collect(Collectors.toList()); 
    final List<Future<Void>> futures = executorService.invokeAll(incrementTasks); 
    for (Future<Void> future : futures) { 
     future.get(); 
    } 
    executorService.shutdown(); 
    executorService.awaitTermination(30, TimeUnit.SECONDS); 
    System.out.println("Final value: " + atomicBigInteger.get()); 
    final boolean areEqual = EXPECTED_VALUE.equals(atomicBigInteger.get()); 
    System.out.println("Does final value equal expected? - " + areEqual); 
    } 

    private static Callable<Void> incrementTask(
     final int taskNumber, 
     final AtomicBigInteger atomicBigInteger 
) { 
    return() -> { 
     for (int increment = 0; increment < INCREMENT_AMOUNT; increment++) { 
     atomicBigInteger.incrementAndGet(); 
     } 
     System.out.println("Task #" + taskNumber + " Completed"); 
     return null; 
    }; 

    } 
} 

और उदाहरण चलने से एक निर्गम यहाँ उस वर्ग की तरह लग रहे हैं इसका एक उदाहरण, एक सरल उदाहरण मैं ने लिखा है कि इसे इस्तेमाल करता है के द्वारा पीछा किया है मेरी मशीन पर:

Available processors: 8 
Task #3 Completed 
Task #8 Completed 
Task #7 Completed 
Task #6 Completed 
Task #5 Completed 
Task #2 Completed 
Task #4 Completed 
Task #1 Completed 
Task #9 Completed 
Task #10 Completed 
Task #11 Completed 
Task #13 Completed 
Task #16 Completed 
Task #12 Completed 
Task #14 Completed 
Task #15 Completed 
Final value: 80000000 
Does final value equal expected? - true 
+0

मैं पिछले जवाब को समझता हूं लेकिन यह नहीं। मुझे अभी तक "->" का उपयोग करने का अधिक अवसर नहीं मिला है, इसलिए ऐसा क्यों हो सकता है या नहीं भी हो सकता है। "पिछला" और "एक्स" कहां से आ रहा है? – user447607

+0

@ user447607 जावा 8 ने लैम्ब्डा एक्सप्रेशन पेश किए। ओरेकल से एक ट्यूटोरियल है [यहां जो मैं समझा सकता हूं उससे अधिक व्यापक है] (https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)। मैं उस के माध्यम से जाने की सलाह देते हैं। असल में, 'परमाणु रिफरेंस' पर '* जमा *' विधियां आपको 'बाइनरीऑपरेटर' में पास करने की अनुमति देती हैं जो विधि को परमाणु तरीके से आमंत्रित करती है। 'बाइनरीऑपरेटर' एक '@ FunctionalInterface' है। 'पिछला' और 'x' लागू होने वाली विधि के पैरामीटर हैं। उपर्युक्त मामले में, दोनों पैरामीटर विधि में पारित किए जा रहे हैं। – mkobit

+0

@mkobit: मुझे लगता है कि आपकी वृद्धि विधियों को 'getAndUpdate' /' updateAndGet' के साथ और अधिक स्पष्ट रूप से लिखा जा सकता है: 'bigInteger.getAndUpdate (BigInteger.ONE::add)' – Lii