[英]Java threading and outputstream printinng issue
我正在尝试创建5个不同的线程,并尝试在每次run
方法中将静态变量的计数增加1时在其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);
}
}
您没有控制或确定性的方式来知道哪个将首先执行,仅因为您首先启动了线程并不意味着它将首先运行。
您正在循环执行5个线程,但不能保证第一个将首先运行,第二个将运行第二个,依此类推...
如果确实希望线程按特定顺序运行,则必须执行一些加入或等待/通知逻辑。
Java语言规范7中的第17.4.3-5
章17.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
打印。
您应该阅读Java的并发性。 您永远不应期望线程以任何特定的顺序运行-尤其是在不围绕共享资源同步代码的情况下。 可能想从这里开始 。
调度的线程取决于操作系统,这些线程有时间片
从输出中可以清楚地看到, thread-3
是首先唤醒的thread-3
( 是的,您是对的) ,然后将static 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 ,因此输出为
现在我的问题是,因为线程3首先退出睡眠状态,所以它必须首先被打印,但是线程5首先被打印,并且值也为22
因为有5个线程同时运行,所以给定语句集的执行顺序是不可预测的。 即使线程以3/4/1/5/2
的顺序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
的值可能已被threads 3, 4, 1
StaticTest.sInt
更新为22
。
而且,您应该始终使用对共享变量的同步访问,以便一个线程所做的更改对其他线程可见,并且对于同步访问,共享变量上的操作是原子的:
public static synchronized Integer incrementSInt() {
return StaticTest.sInt++;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.