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

Rya*_*yan 3 java multithreading runnable

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

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--);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我运行代码两次.两个结果如下所示. 第一次:

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
Run Code Online (Sandbox Code Playgroud)

第二次:

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
Run Code Online (Sandbox Code Playgroud)

max*_*777 5

这是多线程程序的正常行为。只有有限数量的线程才能使CPU处于不变状态,具体取决于处理器的容量。在多线程环境中,每个线程都会获得cpu时间,并且此顺序可能是连续的,也可能不是连续的。

您可以使用'synchronized`语句进行​​顺序处理。尽管此程序用于显示多线程的功能,并且使用同步杀死了实际目的,但在某些情况下需要进行同步,例如访问共享资源。

这是赫伯特·希尔德(Herbert schildt)完整引用的几行内容

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

线程可以有五个状态。 线程状态 当线程正在运行时,所有其他线程都在争夺CPU。任何线程(根据它们的优先级)都可以获取CPU。因此,该顺序可能不一定是连续的。这就是为什么每次运行都会随机输出的原因。

  • @Ryan正如Leeror的回答所解释:线程执行交错。一个线程可能使计数器递减,但是在其写入输出日志之前,其他线程可能同时做了其他事情。该/其他线程可能甚至已递减并在第一个线程有机会写入较早的值之前写入了一个较新的值。没有适当的同步,所有这些都是有效的交错。 (2认同)

Lee*_*eor 5

欢迎来到令人惊叹的并行处理世界.使用线程时,除非您使用锁和障碍等同步机制,否则任何人都无法保证如何安排进度.

你在这里要做的是打印一个统一的输出流,据说可以显示线程的进展情况.这意味着您正在合并线程中的打印输出,但您无法分辨这些打印是如何交错的.此外,打印不一定按调用函数的顺序完成,有几层缓冲,更糟糕的是 - 对打印代码的实际调用不是通过读取和减量原子完成的.

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

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

Thread-2:the number of tickets is 16
Thread-0:the number of tickets is 19
Run Code Online (Sandbox Code Playgroud)

线程0实际上首先读取和递减变量,但打印延迟(由于上下文切换或其他任何原因).线程2在其他一些实例已经完成之后运行,但是立即打印它的消息,然后线程0才完成那个早期的实例.
请注意,您在此处看不到线程0在其间打印任何其他值,其下一次迭代已经读取14.

编辑: 进一步详细说明,这是一个可能的交错的小例子.

假设每个线程的机器代码是 - (以伪格式编写)

label:
    load [var] -> rax
    dec rax
    store rax -> [var]
    call print function // implicitly uses rax 
    cmp rax, 0
    jg label  /// jump-if-greater
Run Code Online (Sandbox Code Playgroud)

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

并且假设您有2个线程正在运行.一种可能的交错可能是 -

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              |
Run Code Online (Sandbox Code Playgroud)

它有点过于简单,但它显示了如何获得无序打印的值.任何交错也是可能的,只要在相同的线程内保持顺序.

另请注意,您可以拥有类似的东西

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]    |
...
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您将获得20次打印两次.