JavaRush /جاوا بلاگ /Random-SD /توھان جاوا کي ھڪڙي سلسلي سان خراب نٿا ڪري سگھو: حصو III -...
Viacheslav
سطح

توھان جاوا کي ھڪڙي سلسلي سان خراب نٿا ڪري سگھو: حصو III - تعامل

گروپ ۾ شايع ٿيل
موضوع جي رابطي جي خاصيتن جو هڪ مختصر جائزو. اڳي، اسان ڏٺو ته ڪيئن سلسلا هڪ ٻئي سان هم وقت ساز ٿين ٿا. هن ڀيري اسان انهن مسئلن تي غور ڪنداسين جيڪي پيدا ٿي سگهن ٿا جڏهن ٿريڊس پاڻ ۾ ڳالهائن ٿا ۽ انهن بابت ڳالهائينداسين ته انهن کان ڪيئن بچي سگهجي ٿو. اسان گہرے مطالعي لاءِ ڪجھ مفيد لنڪ پڻ فراهم ڪنداسين. توھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو III - رابطي - 1

تعارف

تنهن ڪري، اسان ڄاڻون ٿا ته جاوا ۾ اهڙا موضوع آهن، جن بابت توهان جائزو ۾ پڙهي سگهو ٿا " Tread Can't Spoil Java: Part I - Threads " ۽ اهي موضوع هڪ ٻئي سان هم وقت ساز ٿي سگهن ٿا، جن کي اسان جائزو ۾ پيش ڪيو آهي. موضوع جاوا کي خراب نٿو ڪري سگھي ” خراب: حصو II - هم وقت سازي . اهو وقت ڳالهائڻ جو وقت آهي ته موضوع ڪيئن هڪ ٻئي سان لهه وچڙ ۾. اهي عام وسيلن کي ڪيئن حصيداري ڪندا آهن؟ هن سان ڪهڙو مسئلو ٿي سگهي ٿو؟

تعطل

سڀ کان وڏو مسئلو Deadlock آهي. جڏهن ٻه يا وڌيڪ سلسلا هميشه لاءِ هڪ ٻئي جو انتظار ڪندا آهن، ان کي Deadlock چئبو آهي. اچو ته " Deadlock " جي تصور جي وضاحت مان Oracle ويب سائيٽ مان هڪ مثال وٺون :
public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s has bowed to me!%n",
                    this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s has bowed back to me!%n",
                    this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse = new Friend("Alphonse");
        final Friend gaston = new Friend("Gaston");
        new Thread(() -> alphonse.bow(gaston)).start();
        new Thread(() -> gaston.bow(alphonse)).start();
    }
}
هتي ڊيڊ لاڪ شايد پهريون ڀيرو ظاهر نه ٿئي، پر جيڪڏهن توهان جي پروگرام جي عمل کي روڪيو ويو آهي، اهو هلائڻ جو وقت آهي jvisualvm: توھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو III - رابطي - 2جيڪڏهن هڪ پلگ ان JVisualVM ۾ نصب ٿيل آهي (ذريعي اوزار -> پلگ ان)، اسان ڏسي سگهون ٿا ته ڪٿي تعطل واقع ٿيو:
"Thread-1" - Thread t@12
   java.lang.Thread.State: BLOCKED
    at Deadlock$Friend.bowBack(Deadlock.java:16)
    - waiting to lock &lt33a78231> (a Deadlock$Friend) owned by "Thread-0" t@11
ٿريڊ 1 ٿريڊ 0 مان بند ٿيڻ جو انتظار ڪري رهيو آهي. ائين ڇو ٿو ٿئي؟ Thread-1عمل شروع ٿئي ٿو ۽ طريقي سان عمل ڪري ٿو Friend#bow. اهو لفظ سان نشان لڳل آهي synchronized، اهو آهي، اسان مانيٽر کي چونڊيندا آهيون this. طريقي جي داخلا تي، اسان کي هڪ ٻي لنڪ ملي ٿي Friend. ھاڻي ھي ٿريڊ Thread-1ڪنھن ٻئي طريقي تي عمل ڪرڻ گھري ٿو Friend، ان ڪري ھن کان لاڪ پڻ حاصل ڪري ٿو. پر جيڪڏهن ٻئي سلسلي (هن صورت ۾ Thread-0) جو طريقو داخل ڪرڻ لاء منظم bow، پوء تالا اڳ ۾ ئي مصروف ۽ Thread-1انتظار آهي Thread-0، ۽ ان جي برعڪس. بلاڪ ڪرڻ ناقابل حل آهي، تنهنڪري اهو مئل آهي، اهو آهي، مئل. ٻئي موت جي گرفت (جنهن کي آزاد نه ٿو ڪري سگهجي) ۽ هڪ مئل بلاڪ جنهن کان ڪو به فرار نه ٿي سگهي. Deadlock جي موضوع تي، توهان وڊيو ڏسي سگهو ٿا: " Deadlock - Concurrency #1 - Advanced Java ".

چريو

جيڪڏهن ڪو Deadlock آهي، ته پوء اتي هڪ Livelock آهي؟ ها، اتي آهي) لائيو لاڪ (Livelock) اهو آهي ته ڌاڙا ٻاهران زنده نظر اچن ٿا، پر ساڳئي وقت اهي ڪجهه به نٿا ڪري سگهن، ڇاڪاڻ ته ... جنهن حالت ۾ اهي پنهنجو ڪم جاري رکڻ جي ڪوشش ڪري رهيا آهن انهن کي پورو نٿو ڪري سگهجي. جوهر ۾، Livelock Deadlock سان ملندڙ جلندڙ آهي، پر سلسلا مانيٽر جي انتظار ۾ سسٽم تي "هنگ" نٿا ڪن، پر هميشه ڪجهه ڪري رهيا آهن. مثال طور:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class App {
    public static final String ANSI_BLUE = "\u001B[34m";
    public static final String ANSI_PURPLE = "\u001B[35m";

    public static void log(String text) {
        String name = Thread.currentThread().getName(); //like Thread-1 or Thread-0
        String color = ANSI_BLUE;
        int val = Integer.valueOf(name.substring(name.lastIndexOf("-") + 1)) + 1;
        if (val != 0) {
            color = ANSI_PURPLE;
        }
        System.out.println(color + name + ": " + text + color);
        try {
            System.out.println(color + name + ": wait for " + val + " sec" + color);
            Thread.currentThread().sleep(val * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Lock first = new ReentrantLock();
        Lock second = new ReentrantLock();

        Runnable locker = () -> {
            boolean firstLocked = false;
            boolean secondLocked = false;
            try {
                while (!firstLocked || !secondLocked) {
                    firstLocked = first.tryLock(100, TimeUnit.MILLISECONDS);
                    log("First Locked: " + firstLocked);
                    secondLocked = second.tryLock(100, TimeUnit.MILLISECONDS);
                    log("Second Locked: " + secondLocked);
                }
                first.unlock();
                second.unlock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        new Thread(locker).start();
        new Thread(locker).start();
    }
}
هن ڪوڊ جي ڪاميابي ان ترتيب تي منحصر آهي جنهن ۾ جاوا ٿريڊ شيڊيولر ٿريڊز کي شروع ڪري ٿو. جيڪڏهن اهو پهريون ڀيرو شروع ٿئي ٿو Thead-1، اسان حاصل ڪنداسين Livelock:
Thread-1: First Locked: true
Thread-1: wait for 2 sec
Thread-0: First Locked: false
Thread-0: wait for 1 sec
Thread-0: Second Locked: true
Thread-0: wait for 1 sec
Thread-1: Second Locked: false
Thread-1: wait for 2 sec
Thread-0: First Locked: false
Thread-0: wait for 1 sec
...
جيئن ته مثال مان ڏسي سگهجي ٿو، ٻنهي موضوعن کي متبادل طور تي ٻنهي تالن کي پڪڙڻ جي ڪوشش ڪئي، پر اهي ناڪام ٿيا. ان کان سواء، اهي تعطل ۾ نه آهن، اهو آهي، بصري طور تي انهن سان هر شيء ٺيڪ آهي ۽ اهي پنهنجو ڪم ڪري رهيا آهن. توھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو III - رابطي - 3JVisualVM جي مطابق، اسان ننڊ جي دورن ۽ پارڪ جي مدت کي ڏسون ٿا (اهو آهي جڏهن هڪ موضوع هڪ تالا تي قبضو ڪرڻ جي ڪوشش ڪري ٿو، اهو پارڪ اسٽيٽ ۾ وڃي ٿو، جيئن اسان اڳ ۾ بحث ڪيو جڏهن موضوع جي هم وقت سازي بابت ڳالهايو ويو ). Livelock جي موضوع تي، توهان هڪ مثال ڏسي سگهو ٿا: " Java - Thread Livelock ".

بُک

بلاڪ ڪرڻ کان علاوه (deadlock ۽ livelock)، اتي هڪ ٻيو مسئلو آهي جڏهن multithreading سان ڪم ڪري رهيو آهي - Starvation، يا "starvation". اهو رجحان بلاڪ ڪرڻ کان مختلف آهي ته موضوعن کي بلاڪ نه ڪيو ويو آهي، پر انهن وٽ صرف هر ڪنهن لاء ڪافي وسيلا نه آهن. تنهن ڪري، جڏهن ته ڪجهه سلسلا سڄي عمل جي وقت تي قبضو ڪن ٿا، ٻين تي عمل نه ٿو ڪري سگهجي: توھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو III - رابطي - 4

https://www.logicbig.com/

هڪ بهترين مثال هتي ملي سگهي ٿو: " جاوا - ٿريڊ اسٽارويشن ۽ فيئرنس ". هي مثال ڏيکاري ٿو ته ٿريڊس ڪيئن ڪم ڪن ٿيون Starvation ۾ ۽ ڪيئن Thread.sleep کان Thread.wait ۾ هڪ ننڍڙي تبديلي لوڊ کي هڪجهڙائي سان ورهائي سگهي ٿي. توھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو III - رابطي - 5

نسل جي حالت

جڏهن multithreading سان ڪم ڪري، اتي هڪ "ريس شرط" جي طور تي هڪ شيء آهي. اهو رجحان حقيقت ۾ آهي ته موضوع پاڻ ۾ هڪ خاص وسيلا حصيداري ڪندا آهن ۽ ڪوڊ اهڙي طرح لکيو ويو آهي ته اهو هن معاملي ۾ صحيح آپريشن لاء مهيا نٿو ڪري. اچو ته هڪ مثال ڏسو:
public class App {
    public static int value = 0;

    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                int oldValue = value;
                int newValue = ++value;
                if (oldValue + 1 != newValue) {
                    throw new IllegalStateException(oldValue + " + 1 = " + newValue);
                }
            }
        };
        new Thread(task).start();
        new Thread(task).start();
        new Thread(task).start();
    }
}
هي ڪوڊ شايد پهريون ڀيرو غلطي پيدا نه ڪري سگھي. ۽ اهو شايد هن وانگر نظر اچي ٿو:
Exception in thread "Thread-1" java.lang.IllegalStateException: 7899 + 1 = 7901
    at App.lambda$main$0(App.java:13)
    at java.lang.Thread.run(Thread.java:745)
جئين توهان ڏسي سگهو ٿا، جڏهن اهو لڳايو ويو هو، newValueڪجهه غلط ٿي ويو ۽ newValueاتي وڌيڪ هئا. ريس اسٽيٽ ۾ ڪجهه سلسلا valueانهن ٻن ٽيمن جي وچ ۾ تبديل ٿيڻ ۾ ڪامياب ٿي ويا. جيئن اسان ڏسي سگهون ٿا، سلسلي جي وچ ۾ هڪ نسل ظاهر ٿيو آهي. هاڻي تصور ڪريو ته اهو ڪيترو ضروري آهي ته پئسن جي ٽرانزيڪشن سان اهڙيون غلطيون نه ڪيون وڃن... مثال ۽ ڊائريگرام پڻ هتي ملي سگهن ٿا: “ جاوا ٿريڊ ۾ نسل جي حالت کي ترتيب ڏيڻ لاءِ ڪوڊ ”.

اُٿلندڙ

موضوعن جي رابطي جي باري ۾ ڳالهائيندي، اهو خاص طور تي لفظ کي ڌيان ڏيڻ جي قابل آهي volatile. اچو ته هڪ سادي مثال ڏسو:
public class App {
    public static boolean flag = false;

    public static void main(String[] args) throws InterruptedException {
        Runnable whileFlagFalse = () -> {
            while(!flag) {
            }
            System.out.println("Flag is now TRUE");
        };

        new Thread(whileFlagFalse).start();
        Thread.sleep(1000);
        flag = true;
    }
}
سڀ کان وڌيڪ دلچسپ ڳالهه اها آهي ته هڪ اعلي درجي جي امڪان سان اهو ڪم نه ڪندو. نئين سلسلي ۾ تبديلي نظر نه ايندي flag. هن کي درست ڪرڻ لاءِ، flagتوهان کي فيلڊ لاءِ ڪي لفظ بيان ڪرڻ جي ضرورت آهي volatile. ڪيئن ۽ ڇو؟ سڀ عمل پروسيسر طرفان ڪيا ويا آهن. پر حساب ڪتاب جي نتيجن کي ڪٿي ذخيرو ڪرڻ جي ضرورت آهي. هن مقصد لاء، پروسيسر تي مکيه ميموري ۽ هارڊويئر ڪيش آهي. اهي پروسيسر ڪيش ياداشت جي هڪ ننڍڙي ٽڪري وانگر آهن ڊيٽا تائين رسائي حاصل ڪرڻ لاءِ تيزيءَ سان مکيه ميموري تائين رسائي. پر هر شيء ۾ پڻ هڪ منفي آهي: ڪيش ۾ ڊيٽا موجوده نه ٿي سگهي ٿي (جيئن مٿي ڏنل مثال ۾، جڏهن پرچم جي قيمت اپڊيٽ نه ڪئي وئي هئي). تنهن ڪري، لفظ volatileJVM کي ٻڌائي ٿو ته اسان اسان جي متغير کي ڪيش ڪرڻ نٿا چاهيون. اهو توهان کي سڀني موضوعن ۾ حقيقي نتيجو ڏسڻ جي اجازت ڏئي ٿو. هي هڪ تمام آسان فارموليشن آهي. هن موضوع تي، اهو انتهائي صلاح ڏني وئي آهي ته " JSR 133 (Java Memory Model) FAQvolatile " جو ترجمو پڙهو . مان توهان کي مواد جي باري ۾ وڌيڪ پڙهڻ جي صلاح ڏيان ٿو “ جاوا ميموري ماڊل ” ۽ “ جاوا وولٽائل ڪي ورڊ ”. ان کان علاوه، اهو ياد رکڻ ضروري آهي ته اهو ڏسڻ جي باري ۾ آهي، ۽ تبديلين جي جوهر بابت نه. جيڪڏهن اسان "ريس جي حالت" مان ڪوڊ وٺون ٿا، اسان IntelliJ Idea ۾ هڪ اشارو ڏسنداسين: هي انسپيڪشن (انسپيڪشن) IntelliJ Idea ۾ شامل ڪيو ويو مسئلو IDEA-61117 جي حصي جي طور تي ، جيڪو 2010 ۾ رليز نوٽس ۾ درج ڪيو ويو هو . volatileتوھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو III - رابطي - 6

ايٽمي

ايٽمي آپريشنز اهي آپريشن آهن جن کي ورهائي نٿو سگهجي. مثال طور، متغير کي قيمت تفويض ڪرڻ جو عمل ايٽمي آهي. بدقسمتي سان، واڌارو هڪ ايٽمي آپريشن ناهي، ڇاڪاڻ ته وڌ ۾ وڌ ٽن عملن جي ضرورت آهي: پراڻي قدر حاصل ڪريو، ان ۾ ھڪڙو شامل ڪريو، ۽ قيمت بچايو. ايٽمي اهميت ڇو آهي؟ واڌ جي مثال ۾، جيڪڏهن هڪ نسل جي حالت ٿئي ٿي، ڪنهن به وقت تي حصيداري وسيلن (يعني، گڏيل قدر) اوچتو تبديل ٿي سگهي ٿي. ان کان علاوه، اهو ضروري آهي ته 64-bit جوڙجڪ پڻ ايٽمي نه آهن، مثال طور long۽ double. توھان وڌيڪ پڙھي سگھوٿا ھتي: " 64-bit ويلز پڙھڻ ۽ لکڻ وقت ايٽمي کي يقيني بڻايو وڃي ". ايٽمي سان مسئلن جو هڪ مثال هيٺ ڏنل مثال ۾ ڏسي سگهجي ٿو:
public class App {
    public static int value = 0;
    public static AtomicInteger atomic = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                value++;
                atomic.incrementAndGet();
            }
        };
        for (int i = 0; i < 3; i++) {
            new Thread(task).start();
        }
        Thread.sleep(300);
        System.out.println(value);
        System.out.println(atomic.get());
    }
}
ايٽمي سان ڪم ڪرڻ لاء هڪ خاص طبقو Integerهميشه اسان کي 30000 ڏيکاريندو، پر valueاهو وقت وقت تي تبديل ٿيندو. هن موضوع تي هڪ مختصر جائزو آهي " جاوا ۾ ايٽمي تبديلين جو هڪ تعارف ". ايٽمي جو بنياد Compare-and-Swap algorithm تي آهي. توھان ان جي باري ۾ وڌيڪ پڙھي سگھوٿا Habré تي مضمون ۾ " Lock-free algorithms - CAS ۽ FAA جو مثال استعمال ڪندي JDK 7 ۽ 8 " يا وڪيپيڊيا تي مضمون ۾ " تبادلي سان مقابلو " بابت. توھان جاوا کي ھڪڙي سلسلي سان برباد نٿا ڪري سگھو: حصو III - رابطي - 8

http://jeremymanson.blogspot.com/2008/11/what-volatile-means-in-java.html

اڳي ٿئي ٿو

اتي هڪ دلچسپ ۽ پراسرار شيء آهي - اڳ ۾ ٿئي ٿو. وهڪري بابت ڳالهائيندي، اهو پڻ ان جي باري ۾ پڙهڻ جي لائق آهي. The Happens Before Relation انهي ترتيب کي ظاهر ڪري ٿو جنهن ۾ ٿريڊن جي وچ ۾ عمل ڏٺو ويندو. ڪيتريون ئي تشريحون ۽ تشريحون آهن. هن موضوع تي تازو رپورٽن مان هڪ آهي هي رپورٽ:
اهو شايد بهتر آهي ته هي وڊيو ان بابت ڪجهه نه ٻڌائي. تنهنڪري مان صرف وڊيو جي لنڪ ڇڏيندس. توھان پڙھي سگھو ٿا " جاوا - سمجھڻ ھپين- اڳ ۾ رشتا ".

نتيجا

هن جائزي ۾، اسان ڌاڳو رابطي جي خاصيتن تي غور ڪيو. اسان انهن مسئلن تي بحث ڪيو جيڪي پيدا ٿي سگهن ٿا ۽ انهن کي ڳولڻ ۽ ختم ڪرڻ جا طريقا. موضوع تي اضافي مواد جي فهرست: #وياچسلاو
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION