1

我正在尝试创建 5 个不同的线程并尝试在run每次将静态变量的计数增加一时在他们的方法中打印一个静态对象

这是程序的示例输出

pool-1-thread-1 Static Value before update 19
Thread going to sleep pool-1-thread-1
pool-1-thread-4 Static Value before update 19
Thread going to sleep pool-1-thread-4
pool-1-thread-3 Static Value before update 19
Thread going to sleep pool-1-thread-3
pool-1-thread-2 Static Value before update 19
Thread going to sleep pool-1-thread-2
pool-1-thread-5 Static Value before update 19
Thread going to sleep pool-1-thread-5
Thread coming out of sleep pool-1-thread-3  StaticTest.sInt 19
Thread coming out of sleep pool-1-thread-4  StaticTest.sInt 19
Thread coming out of sleep pool-1-thread-1  StaticTest.sInt 19
Thread coming out of sleep pool-1-thread-5  StaticTest.sInt 19
Thread coming out of sleep pool-1-thread-2  StaticTest.sInt 19

**pool-1-thread-5  OLD value 22 Static Value after update 23**
pool-1-thread-1  OLD value 21 Static Value after update 22
pool-1-thread-4  OLD value 20 Static Value after update 21
pool-1-thread-3  OLD value 19 Static Value after update 20
pool-1-thread-2  OLD value 23 Static Value after update 24

现在我的问题是,因为线程 3 首先从睡眠中出来,所以它必须首先打印,但是它的线程 5 首先打印,并且它的值也为 22,即静态变量在线程 5 获取它之前增加了三倍,但是为什么我在向我打印递增值时看到随机顺序,它应该以与它们从睡眠中出来的顺序相同的顺序打印,即线程 3/4/1/5/2

请倾诉思绪?我错过了什么,为什么一旦线程在睡眠后恢复运行状态,随机行为

package com.test.concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class StaticTest {

    public static Integer sInt = new Integer(19);

    public static void main(String[] args) {

        ExecutorService es = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            es.execute(new StaticTask());
        }

    }

}

class StaticTask implements Runnable {

    public void run() {

        String name = Thread.currentThread().getName();
        System.out.println(name + " Static Value before update "
                + StaticTest.sInt);
        try {
            System.out.println("Thread going to sleep " + name);
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Thread coming out of sleep " + name + "  StaticTest.sInt " + StaticTest.sInt);

        int local = StaticTest.sInt;
        StaticTest.sInt = new Integer(local + 1);

        System.out.println(name + "  OLD value " + local +" Static Value after update "
                + StaticTest.sInt);

    }
}
4

6 回答 6

1

Java 语言规范 717.4.3-5中的章节处理了哪些类型的操作受“发生前”规则的约束,以及通常可以预期多线程应用程序的执行顺序。

一旦你阅读了这些章节,你就会意识到执行顺序的保证是相当少的。在多线程应用程序的情况下,我们可能认为自然和理所当然的事情通常是无效的。

此外,还有内存模型——您无需同步即可访问变量sInt。这样,您无法保证不同的线程会注意到对象引用已更改。您必须在修改变量的对象/线程之间使用公共锁,以确保其更改甚至可见。

您可以使用synchronized块和静态对象锁来做到这一点:

// in class:
static Object lock = new Object();


// in run():
synchronized(lock) {
    int local =StaticTest.sInt;
    StaticTest.sInt = new Integer(local + 1);
    System.out.println(name + "  OLD value " + local +" Static Value after update "
        + StaticTest.sInt);
}

这样,打印件synchronized将被正确订购。

于 2013-10-29T13:02:01.953 回答
1

您无法控制或确定性地知道哪个将首先执行,仅仅因为您首先启动线程并不意味着它将首先运行......

您在一个循环中执行 5 个线程,但不能保证第一个线程将首先运行,第二个运行第二个,依此类推......

如果您确实希望线程以特定顺序运行,则必须执行一些加入或等待/通知逻辑。

于 2013-10-29T12:59:50.397 回答
0

现在我的问题是,因为线程 3 首先从睡眠中出来,所以它必须首先打印,但是它的线程 5 首先打印,并且它的值也是 22

由于有 5 个线程同时运行,因此给定语句集的执行顺序是不可预测的。即使线程按顺序出现3/4/1/5/2,也不能保证在执行其余语句时会保留相同的顺序。这就是为什么它被称为Asynchronous执行。

threads 3, 4, 1执行语句可能已经发生

int local =StaticTest.sInt;
StaticTest.sInt = new Integer(local + 1);

一个接一个(以任何顺序),然​​后thread 5有机会一次性执行语句:

int local =StaticTest.sInt;
StaticTest.sInt = new Integer(local + 1);
System.out.println(name + "  OLD value " + local +" Static Value after update "
                + StaticTest.sInt);

你已经在控制台上打印了这个:

pool-1-thread-5  OLD value 22 Static Value after update 23

因为静态变量的值StaticTest.sInt可能已经更新22threads 3, 4, 1.

并且您应该始终使用对共享变量的同步访问,以便一个线程所做的更改对其他人可见,并且通过同步访问,对共享变量的操作是原子的:

public static synchronized Integer incrementSInt() {
    return StaticTest.sInt++;
}
于 2013-10-29T13:31:24.553 回答
0

从输出中,很明显thread-3是首先醒来的那个(是的,你是对的)然后增加静态 int

pool-1-thread-5  OLD value 22 Static Value after update 23
pool-1-thread-1  OLD value 21 Static Value after update 22
pool-1-thread-4  OLD value 20 Static Value after update 21
pool-1-thread-3  OLD value 19 Static Value after update 20  --> static counter incr. from 19 to 20
pool-1-thread-2  OLD value 23 Static Value after update 24

在上面的打印语句中,从它们打印的值可以清楚地看出,首先唤醒的线程正在执行增量并打印值。

不可预知的是打印顺序。

如果您认为在低级别,System.out.print语句是Asynchronous,因此输出

于 2013-10-29T13:05:49.740 回答
0

调度的线程取决于操作系统,这些线程有时间片

于 2013-10-29T13:01:51.203 回答
0

您应该阅读 Java 中的并发性。你永远不应该期望线程以任何特定的顺序运行——尤其是在没有围绕共享资源同步代码的情况下。可能要从这里开始

于 2013-10-29T13:01:27.763 回答