首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >线程如何共享创建它们的同一个实例的字段变量?

线程如何共享创建它们的同一个实例的字段变量?
EN

Stack Overflow用户
提问于 2014-04-19 21:38:32
回答 4查看 211关注 0票数 3

我想测试可运行的接口。创建实现接口Runnable的类的实例。然后通过同一个实例创建三个线程。观察线程如何共享实例的字段变量。两个问题: 1。为什么这两个结果不像"20,19,18……1,0“的序列? 2。为什么这两个结果是不同的?(我两次运行代码)代码如下所示:

代码语言:javascript
复制
public class ThreadDemo2 {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TestThread tt = new TestThread();
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        Thread t3 = new Thread(tt);
        t1.start();
        t2.start();
        t3.start();
    }
}
class TestThread implements Runnable {
    public int tickets = 20;
    public void run(){
        while (tickets >= 0){
            System.out.println(Thread.currentThread().getName() + ":the number of tickets is " + tickets--);
        }
    }
}

我运行了两次密码。这两个结果如下所示。第一次

代码语言:javascript
复制
Thread-1:the number of tickets is 20
Thread-2:the number of tickets is 18
Thread-2:the number of tickets is 16
Thread-0:the number of tickets is 19
Thread-2:the number of tickets is 15
Thread-1:the number of tickets is 17
Thread-2:the number of tickets is 13
Thread-0:the number of tickets is 14
Thread-2:the number of tickets is 11
Thread-1:the number of tickets is 12
Thread-2:the number of tickets is 9
Thread-0:the number of tickets is 10
Thread-2:the number of tickets is 7
Thread-1:the number of tickets is 8
Thread-2:the number of tickets is 5
Thread-0:the number of tickets is 6
Thread-2:the number of tickets is 3
Thread-1:the number of tickets is 4
Thread-2:the number of tickets is 1
Thread-0:the number of tickets is 2
Thread-1:the number of tickets is 0

第二次

代码语言:javascript
复制
Thread-0:the number of tickets is 19
Thread-2:the number of tickets is 18
Thread-2:the number of tickets is 16
Thread-2:the number of tickets is 15
Thread-1:the number of tickets is 20
Thread-2:the number of tickets is 14
Thread-2:the number of tickets is 12
Thread-2:the number of tickets is 11
Thread-0:the number of tickets is 17
Thread-2:the number of tickets is 10
Thread-2:the number of tickets is 8
Thread-1:the number of tickets is 13
Thread-1:the number of tickets is 6
Thread-1:the number of tickets is 5
Thread-2:the number of tickets is 7
Thread-0:the number of tickets is 9
Thread-2:the number of tickets is 3
Thread-1:the number of tickets is 4
Thread-2:the number of tickets is 1
Thread-0:the number of tickets is 2
Thread-1:the number of tickets is 0
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-04-19 21:53:32

欢迎来到并行处理的神奇世界。当使用线程时,除非您使用锁和屏障等同步机制,否则没有人能够保证它们的进度是如何调度的。

您在这里要做的是打印一个统一的输出流,据推测显示线程是如何进行的。这意味着您正在合并来自线程的打印文件,但您无法知道这些打印文件是如何交织的。此外,打印不一定按照调用函数的顺序进行,有几个缓冲层,更糟糕的是,对打印代码的实际调用并不是通过读取和减少来完成的。

您可以说变量是反复减少的(虽然它没有使用任何原子/同步机制,您甚至不能确定您不会看到重复的结果和减少的结果会被覆盖),而且每个线程在每次打印一个较低的值之后都不会打印一个更高的值,但是在线程之间,消息可能会被停止,因此无法按顺序打印。

当你在第一个例子中看到-

代码语言:javascript
复制
Thread-2:the number of tickets is 16
Thread-0:the number of tickets is 19

线程0实际上首先读取和减少了变量,但是打印延迟了(由于上下文切换或其他原因)。线程2运行在其他几个实例之后,但是必须立即打印它的消息,然后线程0才能完成早期的实例。

注意,这里没有看到线程0在中间打印任何其他值,它的下一次迭代已经读到了14。

编辑:再详细介绍一下,下面是一个可能交织的小例子。

假设每个线程的机器代码是-(由伪格式组成)

代码语言:javascript
复制
label:
    load [var] -> rax
    dec rax
    store rax -> [var]
    call print function // implicitly uses rax 
    cmp rax, 0
    jg label  /// jump-if-greater

(var是一个内存位置,在堆栈上用于例如)

假设你有两个线程在运行。一种可能的交织可能是-

代码语言:javascript
复制
thread 0              |   thread 1
------------------------------------
load [var] -> rax     |                          // reads 20
dec rax               |
store rax -> [var]    |
                      |  load [var] -> rax       // reads 19
                      |  dec rax         
                      |  store rax -> [var]
                      |  call print function     // prints 19
                      |  cmp rax, 0           
                      |  jg label 
call print function   |                          //prints 20
cmp rax, 0            |
jg label              |

这有点过于简单化了,但它显示了如何将值按顺序打印出来。任何交织也是可能的,只要在同一线程内保持顺序。

还请注意,您可以拥有以下内容

代码语言:javascript
复制
thread 0              |   thread 1
------------------------------------
load [var] -> rax     |                          // reads 20
dec rax               |
                      |  load [var] -> rax       // reads 20 again !!!
                      |  dec rax         
                      |  store rax -> [var]
store rax -> [var]    |
...

在这种情况下,你将得到20印两次。

票数 5
EN

Stack Overflow用户

发布于 2014-04-19 21:47:47

这是多线程程序的正常行为.根据处理器的容量,只有数量有限的线程才能在instatnt上获得CPU。在多线程环境中,每个线程都获得cpu时间,这个顺序可能是顺序的,也可能不是顺序的。

您可以使用“synchronized`”语句进行顺序处理。虽然该程序用于显示多线程的威力,而使用同步则会扼杀实际用途,但有时需要同步,例如访问共享资源。

这里有几行来自herbert 的完整参考

Java是设计用来在广泛的环境中工作的。这些环境中有些实现多任务的方式与其他环境根本不同。为了安全起见,共享相同优先级的线程应该偶尔产生控制。这确保了所有线程都有机会在非抢占式操作系统下运行。实际上,即使在非抢占环境中,大多数线程仍然有机会运行,因为大多数线程不可避免地遇到一些阻塞情况,例如等待I/O时,阻塞线程被挂起,其他线程可以运行。但是,如果您想要顺利执行多线程,最好不要依赖它。另外,某些类型的任务是CPU密集型的.这样的线程控制着CPU。

一个线程可以有五个状态。

当线程运行时,所有其他线程都在竞争获得CPU。任何线程(根据它们的优先级)都可以获得CPU。这样订单就不一定是连续的了。这就是为什么每次运行都会得到随机输出的原因。

票数 5
EN

Stack Overflow用户

发布于 2014-04-19 21:53:44

所以你有三个线程都在一个变量上工作。我想这是故意的。

这就是我们所说的数据竞赛。每个线程内部都有几个操作:读取变量、测试变量、读取变量、减去变量、将变量写回、打印变量。这些情况可以在线程之间以任意顺序发生。如果它们中的两个人在其中一个打印它之前将其减去,那么您将跳过这个数字。如果其中一个在打印前被延迟了一点,那么这些数字就会不正常。

如果在读取和写入变量的代码周围添加一个synchronized (this)块,则一次只允许一个线程在tt上运行该代码。确保将循环条件和打印放在一个块中,但不要将整个循环放在块中,否则一个线程将完成所有工作。

代码语言:javascript
复制
while (true) 
    synchronized (this) {
        if (tickets >= 0) {
            System.out.print  (Thread.currentThread().getName());
            System.out.println(":the number of tickets is " + tickets--); 
         } else
            break;
    }
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23175867

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档