class A
{
    int i = 0;
    List<Integer> a = new ArrayList<Integer>();    int f1(int j) {
        return a.get(j);
    }
    int f2(int j) {
        return j + a.get(j);
    }
    List<Integer> f3() {
        return a;
    }
    List<Integer> f4() {
        List<Integer> b = new ArrayList<Integer>();
        b.addAll(a);
        return b;
    }
}同一个A对象被多线程并发,f1,f2,f3,f4哪个需要加锁?

解决方案 »

  1.   

    public class Case extends Thread {
     int index=0;
     String[] str={"you","are","me","me","are","you"};
     public void run() {
      while (true) {
       System.out.println(str[index++%6]);
       try {
        Thread.sleep(1000);
       } catch (Exception e) {
        e.printStackTrace();
       }
      }
     }
     
     public static void main(String[] args) {
      new Case().start();
     }
    }
    你可以在f1中 写while (true)  Thread.sleep(1000)等待一下
      

  2.   

    楼主的 a这个list根本一直是空的,除非有调用f3修改。是否需要加锁是跟具体业务寻求,运行场景有关;估计 你的例子中,f1--f4,全部要加锁。形如:
    int synchronized f1(int j) {
      return a.get(j);
      }
      

  3.   

    例子只是个示意,能够说明意思就可以了。
    f1,f2,f3应该是不用的,因为每个线程有自己的thread stack。f4需要
    但我不是很确定。因为java的线程机制,网上说的很清楚的不多。
      

  4.   

    楼主举的例子不好,例子里面a永远都是空的,这样b永远也是空的,所以加锁不加锁都没问题。线程中,如果存在修改可能导致读取到不同的数据,就需要加锁。比如下面的类,如果在不同的线程中分别调用f1和f2,就需要加锁。class A
    {
      int a
      
      int f1() {
      return a;
      }  int f2(int j) {
      a=a+j;
      return a;
      }
    }
      

  5.   

    arrayList是线程不安全的,都要
      

  6.   

    也就是说,技术上你可以选择加,这样肯定不会出错.但业务上你要通过业务流程分析,得出哪个需要加,那个不需要加的结论.不需要加的你加了,顶多是影响性能.应该也不会对系统有影响.如果是我,我会在f1,f2,f4上加,f3不加.
      

  7.   

    f1,f2无论怎么被并发,都不会出错吧。永远都不会把别人的j作为idx去a里取东西返回给自己
    至于f4,其实也是可以被并发的。关键看对b的操作有没有用到共享数据。如果没有,就无需加锁
      

  8.   

    示例确实有点不太清楚的地方,a这个collection是常量,不会被改
      

  9.   

    你那几个方法都是从list里读取值,不用加锁.
    如果有个线程在动态修改list里的值,只需要在修改这个list时,对这个list加个锁就行了,防止只修改了一半,未修改完的情况下,其它线程读取到脏数据.
      

  10.   

    setday  setday  setday  线程不太懂。
      

  11.   

    class A
    {
      int i = 0;
      List<Integer> a = new ArrayList<Integer>();
       
      void f0(int i){
       a.add(i);
    }
      int f1(int j) {
      return a.get(j);
      }
      int f2(int j) {
      return j + a.get(j);
      }
      List<Integer> f3() {
      return a;
      }
      List<Integer> f4() {
      List<Integer> b = new ArrayList<Integer>();
      b.addAll(a);
      return b;
      }
    }加上一个添加方法后就都需要加锁了, 都是为了防止脏读 ,其实这样可以联想到读写锁 , 你应该学过操作系统吧  就累死于读者写着问题。读写加互斥锁,读锁只要有一个在读那所有读锁都可以加入。
      

  12.   


    jvm 是这样管理内存的 ,创建的对象都是保存在 堆中的,java栈是线程独享的,每个java栈由栈帧组成,存储线程中java方法的调用状态,包括局部变量,被调用时传进来的参数,返回值,以及运算的中间结果。堆中的对象是被共享的在JVM中每个对象都有一个对象锁,具体的锁操作看书本吧。 ArrayList 是线程不安全的容器,所以在list上执行的操作都需要加锁。  CopyOnWriteArrayList 实现了无锁容器~~ 你可以看一下。  
      

  13.   

    不需要,因为这里list a没有做修改。
    引api的一段话:
    “如果多个线程同时访问一个 ArrayList 实例,而其中至少一个线程从结构上修改了列表,那么它必须 保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)这一般通过对自然封装该列表的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法将该列表“包装”起来。这最好在创建时完成,以防止意外对列表进行不同步的访问:        List list = Collections.synchronizedList(new ArrayList(...)); ”
      

  14.   

    LZ当前的写法在没有对调用者作出约束前没有办法保证线程安全
    只分析与f1~f4有关的成员变量a
    A声明是package可见的,同包下的类可以访问,并且可以直接访问A.a
    假设类B和A在同一个包下,B在多线程环境下使用A的实例
    而A.a本身不是线程安全的,所以无论怎么对f1~f4加锁,都没有办法
    保证B通过A.a的方式拿到a的引用从而修改a的内容,导致A中所有加锁的地方都是徒劳
    因为A中加锁,无非针对A或A.a,但是都没有办法阻止B的A.a操作
      

  15.   

    如果get方法的返回值的状态不仅仅是靠传入的参数决定的,那么 f1和f2 都要用synchronized,其它都不用。
      

  16.   

    都是读方法,不用加锁的,你可以把成员变量申明为final,没有set方法,就不用加锁了。