线程执行不可预测

时间:2016-10-04 09:28:58

标签: java multithreading

我开始两个线程T1打印1,3,5,.....和线程两个T2打印2,4,6 ....下面是执行它的代码。

CREATE TABLE accommodation_service(
id ............,
accommodation_id ............,
service_id ............,
FOREIGN KEY (accommodation_id)
    REFERENCES accommodation(id)
    ON DELETE CASCADE,
FOREIGN KEY (service_id)
        REFERENCES service(id)
        ON DELETE CASCADE,

)

输出如下:

    public class T1T2Print {
    public static void main(String[] args) {
        Counter c = new Counter();
        c.count = 1;
        printThread t1 = new printThread(c);
        Thread T1 = new Thread(t1);
        T1.setName("T1");
        printThread t2 = new printThread(c);
        Thread T2 = new Thread(t2);
        T2.setName("T2");
        T2.start();
        T1.start();
    }

}

class printThread implements Runnable {
    public Counter count;
    public int reminder;



public printThread(Counter count) {
    this.count = count;
}

public void run() {
    for (int i = 1; i <= 5; i++) {
        synchronized (count) {
            String name = Thread.currentThread().getName();

            if (count.count % 2 != 0 && !name.equals("T1")) {
                try {
                    count.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println(name + " :: " + count.count);
                count.count++;
                count.notify();
            }

            if (count.count % 2 == 0 && !name.equals("T2")) {
                try {
                    count.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println(name + " :: " + count.count);
                count.count++;
                count.notify();
            }

        }
    }
}
}

但是当我将启动线程的顺序更改为

T1 :: 1
T2 :: 2
T1 :: 3
T2 :: 4
T1 :: 5
T2 :: 6
T1 :: 7
T2 :: 8
T1 :: 9
T2 :: 10

输出更改如下,为什么会出现此行为

 T1.start();
 T2.start();

2 个答案:

答案 0 :(得分:2)

让我们在System.out.println(name + " :: waits");来电之前添加count.wait()

然后是第一个案例日志:

T2 :: waits
T1 :: 1
T1 :: waits
T2 :: 2
T2 :: waits
T1 :: 3
T1 :: waits
T2 :: 4
T2 :: waits
T1 :: 5
T1 :: waits
T2 :: 6
T2 :: waits
T1 :: 7
T1 :: waits
T2 :: 8
T2 :: waits
T1 :: 9
T1 :: waits
T2 :: 10

第二案例日志:

T1 :: 1
T1 :: waits
T2 :: 2
T2 :: 3
T1 :: 4
T1 :: 5
T1 :: 6
T1 :: 7
T1 :: 8
T1 :: 9
T1 :: 10
T1 :: 11
T2 :: 12
T2 :: 13
T2 :: 14
T2 :: 15
T2 :: 16
T2 :: 17
T2 :: 18

正如你在第二种情况下所看到的,线程不会睡觉。为什么?因为在第二种情况下,每个线程增加计数器两次(第一次if一次,第二次if一次)(并以同步方式执行)。 (我可以假设你没有想要这个逻辑,而这就是你犯的错误。)案例之间有什么区别?在第一种情况下T2更早开始并且在第一种情况下等待,如果被通知则它被唤醒并且进入第二种情况,即它每次迭代仅增加(并打印)计数器一次。这就是原因。

答案 1 :(得分:1)

if条件存在一些问题。对于每次迭代,计数器可以递增两次。

举一个例子,当计数器为2时,你的T2线程,将值递增两次。 count.count % 2 != 0为false,因此执行else并且计数器递增。然后,第二个if也是false,因为counter现在是3并且count.count % 2 == 0是false,所以第二个else也被执行并且计数器增加并且你得到意外的输出。

您的if语句可以修改为以下内容:

public void run() {
    for (int i = 1; i <= 5; i++) {
        synchronized (count) {
            String name = Thread.currentThread().getName();

            if (name.equals("T1")) {
                while (count.count % 2 == 0) {
                    try {
                        count.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(name + " :: " + count.count);
                count.count++;
                count.notify();
            } else if (name.equals("T2")) {
                while (count.count % 2 != 0) {
                    try {
                        count.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } 
                System.out.println(name + " :: " + count.count);
                count.count++;
                count.notify();
            }
        }
    }
}