【问题标题】:Accessing private access variable data from different class从不同的类访问私有访问变量数据
【发布时间】:2026-02-09 18:15:02
【问题描述】:

我的项目中有这个线程,它不断运行并接受新符号

public class StreamThread extends Thread {
    private Set<String> allSymbolSet = new HashSet<String>(Arrays.asList("USBC", "TCSD", "PCLJ"));
    private PriorityBlockingQueue<String> priorityBlocking = new PriorityBlockingQueue<String>();

    public void addSymbols(String str) {
        if (str != null) {
            priorityBlocking.add(str);
        }
    }

    public void run() {
        while (true) {
            try {
                while (priorityBlocking.peek() != null) {
                    String symbol = priorityBlocking.poll();
                    allSymbolSet.add(symbol);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

我的问题是,我想从另一个类访问变量 allSymbolSet

从另一个类访问这个名为allSymbolSet 的变量的最佳方法是什么,为此我有两个选择

  1. 将 allSymbolSet 的访问说明符从私有修改为默认。

  2. 编写一个用于返回 Set 的 get 方法

请建议我,在这种情况下有什么好的方法??

【问题讨论】:

  • 更有可能是2,但是为什么其他类尝试访问allSymbolSet,它想用它做什么?

标签: java


【解决方案1】:

最好的方法是 getter 方法并同步对对象 allSymbolSet 的访问,如下所示:

public Set<String> getAllSymbolSet() {
    synchronized(allSymbolSet) {
        return allSymbolSet;
    }
}

并在你的线程中同步对allSymbolSet的访问。

【讨论】:

  • 非常感谢,但为什么我需要同步呢?在这种情况下是否需要同步??
  • 是的,因为你将有两个线程访问同一个 HashSet,HashSet 不是线程安全的
  • 如果只想读取字段,真的需要同步吗?
  • @AtishDipongkor 更好,因为在第一种情况下,您无法控制使用您的字段的内容,使用吸气剂您可以更好地控制如何共享您的对象。这种情况下需要同步访问
  • @NishantShreshth 是的,您需要同步才能阅读。或者考虑一个线程安全(er)实现*.com/questions/6992608/…
【解决方案2】:

几个cmets:

  • 如果您将设置设为非私有,则某些代码可能会修改它(错误地或故意地),这可能会导致 StreamThread 类中的行为不一致。不要那样做。
  • 提供简单的 getter 并不能解决上述问题。最好退回您的套装副本。
  • 在多线程环境中尽可能将变量设为最终变量 - 它解决了许多线程安全问题。
  • 宁愿实现 Runnable 而不是扩展 Thread
  • 您将需要同步对您的集合的所有访问(读取和写入),例如使用synchronizedSet,甚至更好地包装ConcurrentHashMap,这通常会提供更好的性能。
  • 您可以在队列中简单地使用take 而不是peek+poll

所以你的最后一课可能是这样的:

public class StreamTask implements Runnable {

    private final Set<String> allSymbolSet;
    private final PriorityBlockingQueue<String> priorityBlocking = new PriorityBlockingQueue<String>();

    public StreamTask() {
         String[] symbols = {"USBC", "TCSD", "PCLJ"};
         //use a thread safe set, for example based on ConcurrentHashMap
         allSymbolSet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean> ());
         Collections.addAll(allSymbolSet, symbols);
    }

    public void addSymbols(String str) {
        if (str != null) {
            priorityBlocking.add(str);
        }
    }

    public Set<String> getSymbols() {
        return new HashSet<> (allSymbolSet); //return a copy
    }

    public void run() {
        while (true) {
            try {
                allSymbolSet.add(priorityBlocking.take());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

最后,我可能遗漏了一些东西,但该类看起来等同于更简单的:

public class StreamTask {

    private final Set<String> allSymbolSet;

    public StreamTask() {
         String[] symbols = {"USBC", "TCSD", "PCLJ"};
         //use a thread safe set, for example based on ConcurrentHashMap
         allSymbolSet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean> ());
         Collections.addAll(allSymbolSet, symbols);
    }

    public void addSymbols(String str) {
        if (str != null) {
            allSymbolSet.add(str);
        }
    }

    public Set<String> getSymbols() {
        return new HashSet<> (allSymbolSet); //return a copy
    }
}

【讨论】:

  • +1 用于在返回 Set 时创建副本。很好的答案。
  • 如果我使用 final 关键字,它会允许它修改 Set 和 PriorotyBlockingQueue 吗??
  • @Kiran 您可以添加到最终集或从中删除,但不能重新分配它。所以你不能写:allSymbolSet = new HashSet&lt;&gt;()建后。
  • 非常感谢,还有一个问题,我正在以这种方式创建 allSymbolSet,public Set allSymbolSet= Collections.synchronizedSet(new HashSet,我还需要同步 getter 方法吗??
  • @Kiran 如果您像我建议的那样返回一个副本,那么是的,您需要同步,因为副本创建涉及需要手动同步的迭代。这就是为什么我建议在 ConcurrentHashMap 周围使用包装器的原因之一,它有一个线程安全的迭代器。
【解决方案3】:

更好的方法是方法 2。编写一个 getter 方法。如果您想允许设置值,请稍后使用设置器。然后你的数据将被封装。

【讨论】:

    【解决方案4】:

    编写一个应该返回 Set 的 get 方法。通过使用它,您的 private 仍然是私有的,并且您还可以使用同一类的 Object 从外部访问它。

    【讨论】: