V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mart1nN
V2EX  ›  Java

Java 多线程资源共享的问题

  •  
  •   mart1nN · 2019-04-11 14:44:44 +08:00 · 2684 次点击
    这是一个创建于 2088 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如一个 ThreadDemo 类实现了 Runnable 接口,我在 main 函数中创建了一个 ThreadDemo 类实例,并用这个实例创建了两个线程。 当我在 ThreadDemo 中定义了一个 private int count 属性时,请问这个变量在两个线程栈中都有存储吗?还是说这个变量存储在另外的地方?如果我在 ThreadDemo 中定义了 private String name 时又是怎么样的?

    17 条回复    2019-04-13 08:37:01 +08:00
    momocraft
        1
    momocraft  
       2019-04-11 14:59:44 +08:00
    field 是对象的一部分,一定不在栈上,**对象的引用** 才可能在栈上

    这已经和线程没关系了..
    misaka19000
        2
    misaka19000  
       2019-04-11 15:07:27 +08:00 via Android
    上代码
    gosansam
        3
    gosansam  
       2019-04-11 15:15:42 +08:00
    个人感觉共享变量存在主存中,各个使用会去拉副本到线程栈
    liuxey
        4
    liuxey  
       2019-04-11 15:19:46 +08:00
    首先你的案例里没有资源共享的情况,两个 ThreadDemo 实例都有自己的 count,各不相干
    其次 String 也是一样的
    gaius
        5
    gaius  
       2019-04-11 15:26:59 +08:00
    new 一个实例是怎么开的 2 个线程?
    lhx2008
        6
    lhx2008  
       2019-04-11 15:30:14 +08:00
    栈上面就没有共享的事情
    mart1nN
        7
    mart1nN  
    OP
       2019-04-11 15:45:02 +08:00
    ThreadDemo

    public class ThreadDemo implements Runnable{

    private int count;

    public ThreadDemo() {
    count = 0;
    }

    @Override
    //循环打印
    public void run() {
    System.out.println("before syn");
    synchronized (this){
    for (int i = 0; i < 10; i++){
    try {
    System.out.println(Thread.currentThread().getName() + ":" + (count++));
    Thread.sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    System.out.println("after syn");
    }

    }


    test

    //这里就写主要部分了
    ThreadDemo threadDemo = new ThreadDemo();
    Thread t1 = new Thread(threadDemo, "thread-01");
    Thread t2 = new Thread(threadDemo, "thread-02");
    t1.start();
    t2.start();

    t1,t2 这两个线程栈里存储了 threadDemo 实例的引用吗?还有就是 main 线程会给这两个线程初始化后这三个线程栈存储的有啥不同吗?
    misaka19000
        8
    misaka19000  
       2019-04-11 15:55:35 +08:00
    应该只有 main 线程的栈中持有对 threadDemo 对象的引用吧
    Michlix
        9
    Michlix  
       2019-04-11 16:07:31 +08:00
    domty
        10
    domty  
       2019-04-11 16:17:37 +08:00
    对象都是分配在堆上的
    即便是线程持有的也只是这个对象的一个引用
    hailiang88
        11
    hailiang88  
       2019-04-11 16:29:26 +08:00
    1、如果是类的成员变量的话会存在 Method Area,这个区域是线程共享的
    2、如果是局部变量的话会存在于 Stack Area,可以了解下栈帧( stack frame ),这个是线程私有的
    [https://liang.im/index.php/archives/3/]( https://liang.im/index.php/archives/3/)
    DanielGuo
        12
    DanielGuo  
       2019-04-11 16:30:51 +08:00
    @mart1nN 在我的理解中,t1 和 t2 都是一个对象,这两个对象都在堆中存在,t1 和 t2 的实例变量有对 threadDemo 的引用
    zifangsky
        13
    zifangsky  
       2019-04-11 17:55:25 +08:00
    1、堆、栈的概念跟线程没有关系。
    2、对象在堆空间( Heap space )实例化,成员变量 count 自然也在堆空间中,且 new 一次只实例化一次。
    3、之所以多个线程对成员变量 count 执行写操作会有线程安全问题,那是因为多个线程在对同一片内存区域的 count 变量进行操作。
    v2qwsdcv
        14
    v2qwsdcv  
       2019-04-11 19:45:03 +08:00
    源码给您找好啦,求别鄙视我们做 C++的了 都不容易。


    https://github.com/unofficial-openjdk/openjdk/blob/5ec14c8bb2533c20eca3564258c4dc66bf3bb9c3/src/java.base/share/classes/java/lang/Thread.java
    public synchronized void start() {
    ...
    try {
    start0();
    started = true;
    } ...
    }

    private native void start0();

    https://github.com/unofficial-openjdk/openjdk/blob/531ef5d0ede6d733b00c9bc1b6b3c14a0b2b3e81/src/java.base/share/native/libjava/Thread.c
    {"start0", "()V", (void *)&JVM_StartThread},

    https://github.com/unofficial-openjdk/openjdk/blob/e19d12112815026f04a9df075e56eb26622b9d8d/src/hotspot/share/prims/jvm.cpp

    JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
    JVMWrapper("JVM_StartThread");
    JavaThread *native_thread = NULL;
    ...
    native_thread = new JavaThread(&thread_entry, sz);

    if (native_thread->osthread() != NULL) {
    // Note: the current thread is not being used within "prepare".
    native_thread->prepare(jthread);
    }
    }
    }
    ...
    Thread::start(native_thread);

    JVM_END

    static void thread_entry(JavaThread* thread, TRAPS) {
    HandleMark hm(THREAD);
    Handle obj(THREAD, thread->threadObj());
    JavaValue result(T_VOID);
    JavaCalls::call_virtual(&result,
    obj,
    SystemDictionary::Thread_klass(),
    vmSymbols::run_method_name(),
    vmSymbols::void_method_signature(),
    THREAD);
    }

    https://github.com/unofficial-openjdk/openjdk/blob/294d2d319b870ac68ca10a5b03006a70e26bcaba/src/hotspot/share/runtime/thread.cpp
    void JavaThread::run() {
    ...
    thread_main_inner();
    }


    void JavaThread::thread_main_inner() {
    ...
    if (!this->has_pending_exception() &&
    !java_lang_Thread::is_stillborn(this->threadObj())) {
    {
    ResourceMark rm(this);
    this->set_native_thread_name(this->get_thread_name());
    }
    HandleMark hm(this);
    this->entry_point()(this, this);
    }
    ...
    }

    https://github.com/unofficial-openjdk/openjdk/blob/d148cecb01572f077179c94cb59117af89eb59b8/src/hotspot/share/runtime/javaCalls.cpp

    https://github.com/unofficial-openjdk/openjdk/blob/e836a10c8c6ed2ef2f3219c46ca1906a2d9d6493/src/hotspot/share/classfile/vmSymbols.hpp
    template(run_method_name, "run")
    v2qwsdcv
        15
    v2qwsdcv  
       2019-04-11 20:01:10 +08:00
    另 #13 说的对
    troywinter
        16
    troywinter  
       2019-04-12 00:32:12 +08:00
    看了这个帖子我还以为搞了十年的 PLT 都是假的,直到看到了#13 楼
    gramyang
        17
    gramyang  
       2019-04-13 08:37:01 +08:00
    兄啊,刚刚测试了一下,这个 demo 并不会出现乱序
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1253 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:01 · PVG 02:01 · LAX 10:01 · JFK 13:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.