8

在我设计的示例中,关于 teamMembers 列表对线程安全有什么影响?

我可以依靠run()方法看到的列表状态保持一致吗?

假设

  1. setATeamMembers方法只被调用一次,在 spring 创建ATeamEpisodebean时

  2. init方法在 #1 之后由 spring (init-method) 调用

  3. ATeamMember类是不可变的

    • 我需要申报teamMembers volatile或类似吗?

    • 我忽略了这种方法还有其他可怕的问题吗?

如果这很明显,或者 rtfm 明显失败,请道歉

谢谢并恭祝安康

埃德

package aTeam;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ATeamEpisode implements Runnable{

    private List<ATeamMember> teamMembers;

    /* DI by spring */
    public void setATeamMembers(List<ATeamMember> teamMembers){
        this.teamMembers = new ArrayList<ATeamMember>(teamMembers);    
    }

    private Thread skirmishThread;

    public synchronized void init(){
        System.out.println("Starting skirmish");
        destroy();
        (skirmishThread = new Thread(this,"SkirmishThread")).start();
    }
    public synchronized void destroy(){
        if (skirmishThread != null){
            skirmishThread.interrupt();
            skirmishThread=null;
        }
    }

    private void firesWildlyIntoTheAir(ATeamMember teamMember){
        System.out.println(teamMember.getName()+" sprays the sky..");
    }

    @Override
    public void run() {
        try {
            Random rnd = new Random();
            while(! Thread.interrupted()){
                firesWildlyIntoTheAir(teamMembers.get(rnd.nextInt(teamMembers.size())));
                Thread.sleep(1000 * rnd.nextInt(5));
            }
        } catch (InterruptedException e) {
            System.out.println("End of skirmish");
            /* edit as per Adam's suggestion */
           // Thread.currentThread().interrupt();
        }
    }
}
4

2 回答 2

5

如果,如您所说, setATeamMembers 仅调用一次,并且您的代码的任何其他部分都没有替换此集合,那么将其设置为 volatile 是没有意义的。Volatile 表示一个成员可以被不同的线程写入。

考虑到您的代码似乎也没有任何部分正在更新此集合,您可能需要考虑使集合显式不可修改,例如使用 Collections.unmodifiableList()。这让您和其他人清楚,该集合不会被修改,并且如果您尝试修改它,则会在您的脸上抛出一个大异常。

Spring 的延迟初始化是,AFAIR,线程安全的。

于 2012-10-08T12:06:25.927 回答
3

也许。这样的List接口不是线程安全的,无论你做什么,它都不能在消费者端成为线程安全的

您需要做的是创建一个线程安全列表(Java 运行时有几个实现)并将其中一个用于teamMembersbean。

通过字段访问 beanteamMembers不是问题,因为其他线程不会创建新实例,它们会更改 bean 的状态(即内部的数据)teamMembers

因此 bean 必须确保对其内部结构的更改正确同步。

在您的情况下,您将需要一个特殊的列表实现,它从列表中返回一个随机元素。为什么?因为调用时的值teamMembers.size()可能已经改变teamMembers.get()

实现此目的的一种简单方法是在此代码中包装所有方法调用:

 synchronized(teamMembers) { ... }

但你必须确定你真的抓住了所有这些。实现这一目标的最简单方法是,正如我上面所说,编写您自己的列表,其中提供您需要的所有特殊方法。这样,您可以synchronized根据需要使用锁或方法内部。

于 2012-10-08T12:08:29.390 回答