在Java线程中访问变量

3

我的线程类中有一个全局变量,它是一个 ArrayList<>。我从 main() 方法定期运行这个线程;每当该线程运行时,我在 run() 方法中填充此列表。我想从 main() 方法访问此列表,但每当我从 main() 访问此列表时,我得到的是一个空列表。请建议如何实现此目标,以下是我的代码示例。

private static ArrayList<SampleClass> allproxyDetailsPojoList = null;   

public static void main(String[] args) {
    try {
        ScheduledExecutorService threadSchedulerService = Executors.newScheduledThreadPool(1);
        threadSchedulerService.scheduleAtFixedRate(new SampleThreadClass(), 0, 5, TimeUnit.MINUTES);

        Thread.sleep(8000);

        SampleThreadClass sampleThreadClass = new SampleThreadClass();

        // here i am getting list empty
        allproxyDetailsPojoList = sampleThreadClass.getSampleClassList();

    } catch (Exception e) {

    }
}

这是我的线程类:

public class SampleThreadClass implements Runnable {

    ArrayList<SampleClass> sampleClassList = null;

    @Override
    public void run() {
        MyDao myDao = null;
        try {
            myDao = new MyDao();
            sampleClassList = myDao.getSampleClassList();
        } catch (Exception e) {

        }
    }

    public ArrayList<SampleClass> getSampleClassList(){
        return sampleClassList;
    }
}

注意:不能使用static关键字。
5个回答

3

您需要了解类的实例与类定义相对应的概念。在您的代码中,您创建了两个类的实例。由于变量不是静态的,每个实例都有自己的变量实例。您无法通过实例2的引用访问实例1的变量。

try {
    ScheduledExecutorService threadSchedulerService = Executors.newScheduledThreadPool(1);
    threadSchedulerService.scheduleAtFixedRate(new SampleThreadClass(), 0, 5, TimeUnit.MINUTES);
                                           //  ^ 1st Instance
    Thread.sleep(8000);

    SampleThreadClass sampleThreadClass = new SampleThreadClass(); // <-- 2nd Instance

因此,要使用单个实例,请将其更改为

try {
    ScheduledExecutorService threadSchedulerService = Executors.newScheduledThreadPool(1);

    SampleThreadClass sampleThreadClass = new SampleThreadClass();
    threadSchedulerService.scheduleAtFixedRate(sampleThreadClass , 0, 5, TimeUnit.MINUTES);

    Thread.sleep(8000);

但是为了避免“奇怪的行为”,我建议对sampleClassList进行同步访问。

此外,为了避免NPE(NullPointerException),我会进行更改。

public ArrayList<SampleClass> getSampleClassList(){
    return sampleClassList;
}

to

public ArrayList<SampleClass> getSampleClassList(){
    if ( null != sampleClassList )
        return sampleClassList;
    return Collections.emptyList();
}

请注意,这个空列表是不可变的。如果你需要一个可变的列表,你可以返回一个new ArrayList<SampleClass>()

附言

使用sleep并不能使这段代码变得稳定。如果你期望在至少执行一次服务调用后存在一个列表,那么你应该这样做,而不是等待一个固定的时间周期。但这就是另一个问题了。


2
您正在阅读您在 中创建的对象列表。
SampleThreadClass sampleThreadClass = new SampleThreadClass();

而不是您在此处创建并执行的内容:

 threadSchedulerService.scheduleAtFixedRate(new SampleThreadClass(), 0, 5, TimeUnit.MINUTES);

This

ArrayList<SampleClass> sampleClassList = null; 

是实例变量,不是全局变量。

我认为你得到的是 null 值而不是空列表。


1

一旦您创建了一个线程类的对象,您需要启动该线程。可以使用start()函数启动线程。 当使用join()函数与start()一起使用时,您的主程序将等待您的线程执行。一旦线程完成执行,您的主程序将开始运行。 如果您不使用join(),则您的主程序将打印allproxyDetailsPojoList,但该列表为空,因为线程未完成执行。


1
在@apadana的答案上添加一些信息(保留已提交给ExecutorService的类的相同实例):
一旦您将任务提交到ExecutorService,您的任务和主线程将并行运行。 主线程直到任务完成才获取该值。由于您在未完成任务的情况下访问变量,因此会得到空值。
不要使用sleep来填充值。而是依赖于 ExecutorService invokeAll API或使用 CountDownLatch 请看工作代码示例:

如何等待生成自己的线程的线程?


他可以等待很久,因为他从一个不同的实例中获取列表,而那个实例是被填充的实例。 - Fildor
实际上,他应该使用已提交给ExecutorService的相同实例。他正在不必要地创建另一个实例。 - Ravindra babu
他正在使用相同的类(class)。他没有使用相同的实例(instance)。在使用这些术语时不够准确是导致新手犯错的原因。 - Fildor
我指的是实例。感谢给我纠正评论的机会。 - Ravindra babu

1
你不需要调用 start(),因为你正在使用执行器服务。所以你现在的做法是正确的。然而,你创建了两个不同的 SampleThreadClass。第一个是在这里创建的:
ScheduledExecutorService threadSchedulerService = Executors.newScheduledThreadPool(1);
        threadSchedulerService.scheduleAtFixedRate(new SampleThreadClass(), 0, 5, TimeUnit.MINUTES);

第二个是在这里创建的,但executorservice并不知道它,因为它从未被传递给它。
SampleThreadClass sampleThreadClass = new SampleThreadClass();

这是如何修复的:

SampleThreadClass sampleThreadClass = new SampleThreadClass();
 ScheduledExecutorService threadSchedulerService = Executors.newScheduledThreadPool(1);
        threadSchedulerService.scheduleAtFixedRate(sampleThreadClass, 0, 5, TimeUnit.MINUTES);

        Thread.sleep(8000);

        // here i am getting list empty
        allproxyDetailsPojoList = sampleThreadClass.getSampleClassList();

在此修复后,下一步应该是将静态变量定义为易于及时在线程之间可见的volatile变量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接