0

In main method, I created 5 threads and start() them all. The problem is, even I added a condition to end all threads, they still run. When I add System.out.print(""); below the loop for, my program works well. This is just an accident because I wanna check if my while is working or not. I changed the position of System.out.print(""); but it couldn't work as before. Can I have some explanations?

public static void main(String args[]) {
    int number = 5;
    Philosopher phils[] = new Philosopher[number];
    Fork Fork[] = new Fork[number];

    for (int i = 0; i < number; i++) {
        Fork[i] = new Fork();
    }

    for (int i = 0; i < number; i++) {
        Fork firstFork = Fork[i];
        Fork secondFork = Fork[(i + 1) % number];
        if (i % 2 == 1) {
            phils[i] = new Philosopher(firstFork, secondFork);

        } else {
            phils[i] = new Philosopher(secondFork, firstFork);
        }
        Thread t = new Thread(phils[i], "Philosopher " + i);
        t.start();
    }
    while (true) {
        try {
            boolean allEatten = true;
            for (Philosopher p : phils) {
                if (!p.eatten) {
                    allEatten = false;
                    break;
                }
            }
            System.out.print("");//It's here
            if (allEatten) {
                System.out.println("Everyone Eats");
                break;
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
    System.out.println("Exit The Program!");
    System.exit(0); }

Here my class Fork

static class Fork {

        public Semaphore sem = new Semaphore(1);

        void grab() {
            try {
                sem.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace(System.out);
            }
        }

        void release() {
            sem.release();
        }

    }

And my class Philosopher

static class Philosopher implements Runnable {
        public boolean eatten = false;
        public Fork firstFork;
        public Fork secondFork;
        public Philosopher(Fork firstFork, Fork secondFork) {
            this.firstFork = firstFork;
            this.secondFork = secondFork;
        }
        @Override
        public void run() {
            try {
                while (true) {
                    doAction(": Thinking");
                    firstFork.grab();
                    doAction(": Picked up the 1st fork");
                    secondFork.grab();
                    doAction(": Picked up the 2nd fork");
                    eatten = true;
                    eat();
                    firstFork.release();
                    doAction(": Put down the 1st fork");
                    secondFork.release();
                    doAction(": Put down the 2nd fork. Back to thinking");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;//đảm bảo thread dừng hẳn khi bị ngắt
            }
        }
        void eat() {
            try {
                int sleepTime = (int) (Math.random() * 1000);
                System.out.println(Thread.currentThread().getName() + " eats for " + sleepTime);
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace(System.out);
            }
        }
        private void doAction(String action) throws InterruptedException {
            System.out.println(Thread.currentThread().getName() + " " + action);
            Thread.sleep(((int) (Math.random() * 100)));
        }
    }
Haru
  • 31
  • 4
  • What prevents one thread from modifying `eaten` while another thread (the one that checks if everyone has eaten) is accessing it? – David Schwartz Mar 07 '22 at 10:13
  • @DavidSchwartz Its Semaphore. Each `Fork` has a semaphore and it has 1 permit. Each `Philosopher` can only access on a `Fork` at a time. If `Philosopher` access on a `Fork`, permit will be 0 and it blocked other thread. – Haru Mar 07 '22 at 11:57
  • Those semaphores don't protect `eatten` -- the loop checks `eatten` without holding each semaphore when it does it. – David Schwartz Mar 08 '22 at 18:31
  • @DavidSchwartz `eatten=true` in the `run()`. Its value changed only when a `Philosopher` could take two `Fork`. – Haru Mar 09 '22 at 06:54
  • The problem is the loop that checks `eatten` without holding the semaphore that protects it. What stops that loop from accessing `eatten` while another thread is modifying it? – David Schwartz Mar 09 '22 at 21:02
  • @DavidSchwartz I couldn't get it. The semaphore doesn't protect `eatten`. It protects a `Fork`. – Haru Mar 10 '22 at 17:40
  • So then what prevents one thread from modifying `eatten` while another thread is accessing it? – David Schwartz Mar 10 '22 at 18:31
  • @DavidSchwartz StackOverflow recommend this link for me: https://stackoverflow.com/questions/25425130/loop-doesnt-see-value-changed-by-other-thread-without-a-print-statement – Haru Mar 11 '22 at 02:13
  • Yes, exactly. The result of concurrent access and modification is unpredictable. You need to use some kind of synchronization on `eatten` such as making it `volatile`. – David Schwartz Mar 11 '22 at 09:28

0 Answers0