public class MyRunnable implements Runnable{
          Integer a=0; //实例变量
           public void run(){
            synchronized(a){
                   for( int i=0;i<10;i++){
                           System.out.println(Thread.currentThread().getName()+" "+a++);
                   }
            }
          }
          public static void main(String args[]){
            MyRunnable  mr=new  MyRunnable ();
            Thread t1=new Thread(mr); //Thread(Runnable r)
            Thread t2=new Thread(mr);
            t1.start();
            t2.start();
          }
}执行结果为:Thread-0 0
Thread-0 2
Thread-1 1
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-1 10
Thread-1 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19看上去,synchronized没有起作用啊!!!
这是怎么回事?

解决方案 »

  1.   

    奇怪了,为什么我的都真正常啊。每次结果都这样,很正常。
    Thread-0 0
    Thread-0 1
    Thread-0 2
    Thread-0 3
    Thread-0 4
    Thread-0 5
    Thread-0 6
    Thread-0 7
    Thread-0 8
    Thread-0 9
    Thread-1 10
    Thread-1 11
    Thread-1 12
    Thread-1 13
    Thread-1 14
    Thread-1 15
    Thread-1 16
    Thread-1 17
    Thread-1 18
    Thread-1 19
      

  2.   

    不会吧!!我在MyEclipse中试过,在DOS中也试过都不是正常的顺序!!为什么呢?/太奇怪了!
      

  3.   

    Integer a = 0;
    改为
    Integer a = new Integer(0);
      

  4.   

    好像第一次看到synchronized这样用,
    synchronized是为了不让多个现成同时访问同一个地方线程 1
               ---》 some object
    线程 2
    楼主你这个虽然加了一个sychronized,那其实就是线程自己在run,根本没有其他线程的互斥访问的概念, 你代码里的
    MyRunnable  mr=new  MyRunnable (); 
    只是充当了我上面提到的特殊的some object你去掉synzheonized看看,其实效果一样的否则你想好了,估计按你的思路,一旦一个线程跑进synchronized块,势必要把for循环跑完才能让第二个线程接着执行for,实际上Thread 0 和 Thread 1是交叉的,不觉得矛盾吗?
      

  5.   

    其实你的运行结果已经说明一切了,
    上面一位兄弟看起来运行结果很整齐的可能机器比较快,Thread 0 瞬间就跑完了
      

  6.   

    这个说的是对的,您要这个效果得把Integer a=0;设置成类变量。你这个对象里每次都锁掉了自己的一个变量,是不会影响到其他对象的。看来我的机器跑的比较快,呵呵。
      

  7.   

    我拿着楼主的代码测试了几十次 大概5次就会出现一次不正确的 其他四次是正确的 
    其实这个原因并不是synchronized没有起作用 而是和环境有关 并且因为数据的原因有时候锁不住
    也有可能 解决的办法就是在中间每次让线程休眠一下public class MyRunnable implements Runnable{ 
              Integer a=0; //实例变量 
              public void run(){ 
                synchronized(a){ 
                      for( int i=0;i <10;i++){ 
                              System.out.println(Thread.currentThread().getName()+" "+a++); 
                      } 
                } 
              } 
              public static void main(String args[]){ 
                MyRunnable  mr=new  MyRunnable (); 
                Thread t1=new Thread(mr); //Thread(Runnable r) 
                Thread t2=new Thread(mr); 
                t1.start(); 
                try {
    Thread.sleep(1);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
                t2.start(); 
              } 
      

  8.   


    都说了
    Integer a = 0; 
    改为 
    Integer a = new Integer(0);
      

  9.   

    这个synchronized是起作用的
    会不会是jdk对互斥机制做过改动,楼主的jdk什么版本?在打印前放了个sleep,下面是打印结果synchronized(a):
    Thread-1 0
    Thread-1 1
    Thread-1 2
    Thread-1 3
    Thread-1 4
    Thread-1 5
    Thread-1 6
    Thread-1 7
    Thread-1 8
    Thread-1 9
    Thread-0 10
    Thread-0 11
    Thread-0 12
    Thread-0 13
    Thread-0 14
    Thread-0 15
    Thread-0 16
    Thread-0 17
    Thread-0 18
    Thread-0 19无互斥段:
    Thread-1 0
    Thread-0 1
    Thread-1 2
    Thread-0 3
    Thread-1 4
    Thread-0 5
    Thread-1 6
    Thread-0 7
    Thread-1 8
    Thread-0 9
    Thread-1 10
    Thread-0 11
    Thread-1 12
    Thread-0 13
    Thread-0 14
    Thread-1 15
    Thread-0 16
    Thread-1 17
    Thread-0 18
    Thread-1 19
      

  10.   


    这和jdk的Integer的缓存机制有关
      

  11.   

    再讲一遍
    Integer a = 0; 
    改为 
    Integer a = new Integer(0);
    就正确了synchronized的这种写法没有问题
      

  12.   

    你这个方法是不对的,如果我开了100个线程,结果就不对。
    后面我在网上搜索发现原因可能是这样的,因为a++这个操作,a的引用就变了,你锁的效果就不在了。而且经过检验确实好像是这样的。你可以再你的类里在加一个Integer b = a;然后你在循环里取看b == a这个情况就不成立了,他们的引用不一样了。
      

  13.   

    jdk1.6 一切正常,LS运行出错的都是什么版本
      

  14.   

    你for循环100次 应该就能发现楼主的synchronized好像没用了 
    其实是有作业的 只不过是在开始的一段时间里
      

  15.   


    别说100次了,运行1000次也正常,而且是在print之前加了Thread.sleep(1),如果互斥没有生效是不可能正常打印的Thread-1 996
    Thread-1 997
    Thread-1 998
    Thread-1 999
    Thread-0 1000
    Thread-0 1001
    Thread-0 1002
    Thread-0 1003
    ……
    ……
      

  16.   


    public class MyRunnable implements Runnable {
    private int[] a = new int[] { 0 }; public void run() {
    synchronized (a) {
    for (int i = 0; i < 10; i++) {
    System.out.println(Thread.currentThread().getName() + " "
    + a[0]++);
    }
    }
    } public static void main(String args[]) {
    MyRunnable mr = new MyRunnable();
    for (int i = 0; i < 100; i++) {
    Thread t = new Thread(mr);
    t.start();
    }
    }
    }
      

  17.   

    顶6楼。
    sychronized是为了防止多线程访问
      

  18.   

    对象锁问题哦,知道为什么叫对象锁吗,之所以叫对象锁,意思是那个锁是对象,而不是引用,引用所指的对象在不停的变化,比如第一次是a所指向的对象是0,即,先假设t1拿到对象0这个对象锁进去了,由于a++后,导致a=1(之间发生了自动装解箱),此时a引用指的对象是1,也就是说synchronized(a)中 此时的对象锁是Integer 1,谁拿到对象1就可以进去了,而正好在这个时候,t1的cpu时间片用完了,t2拿着对象锁 即integer对象1,进来了,当在a++后,对象变成2了,t1接着拿着对象锁3进来了。。交替执行,
    由于对象锁在不停的变化,所以导致了你这个和没锁的样子差不多,谁拿到对象锁而且有CPU时间片就可以进去了
      

  19.   

    根据#25的说法又去试了一下,的确是锁在变
    重新分析一下“
    Thread-0先拿到锁"0",而Thread-1在锁改变之前也去争抢"0",结果就是等待Thread-0运行完互斥段,释放锁"0"下面人为制造了一个延迟,使Thread-1拿到新的锁,结果比较清晰的了public class MyRunnable implements Runnable{
              Integer a = 0; //实例变量
              public void run(){
                synchronized(a){
                      for( int i=0;i <100;i++){
                       try {
    Thread.sleep(500);
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
                              System.out.println(Thread.currentThread().getName()+" "+a++);
                      }
                }
              }
              public static void main(String args[]){
                MyRunnable  mr=new  MyRunnable ();
                Thread t1=new Thread(mr); //Thread(Runnable r)
                Thread t2=new Thread(mr);
                t1.start();            
                try {
    Thread.sleep(3000);
    System.out.println("");
    } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    t2.start();
              }
    }
      

  20.   

    代码有问题,synchronized没有起到任何作用。synchronized中是同步了一个Integer对象引用a
    当执行++时,一个新的Integer对象产生了这时a又引用这个新对象。这样就成了一边获得某个对象同步信号,一边又放弃了当前获得获得的同步信号,又尝试获得新对象的同步信号。正确的作法如下:    public void run() {
            synchronized (this) {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + " " + a++);
                }
            }
        }
      

  21.   

    我不知道楼主是什么意思?  我猜想你是要两个线程交替进行吧?,就是说线程0打印出一个a 然后线程1再打印一个a. 如此交替进行是么.?  如果是这样的话,不仅要运用同步,还要用到线程通信,就是说,当线程0执行完以后,如果在是线程0拿到执行权的话就要他等待.. 用1.5的并发库创建线程池方便点 
    好了 上代码import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;public class ThreadTest {
    public static void main(String[] args) {
    //创建两个线程
    ExecutorService service = Executors.newFixedThreadPool(2);
    final Manager manager = new Manager();

    //执行线程
    service.execute(new Runnable(){ @Override
    public void run() {
    for (int i = 0; i < 10; i++) {
    manager.printA1();
    }

    }

    });
    service.execute(new Runnable(){
    @Override
    public void run() {
    for (int i = 0; i < 10; i++) {
    manager.printA2();
    }

    }

    });

    service.shutdown();//删除所有线程,程序结束
    }
    }/**
     * 业务类.线程执行的方法
     * @author Administrator
     *
     */
    class Manager
    {
    //要打印的变量
    private int a = 0;

    //标识此时是否该A1运行
    private boolean bshouldA1Run = true;

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public void printA1()
    {
    try {
    lock.lock();
    //如果不该A1运行,那么等待.
    if(!bshouldA1Run)
    condition.await();

    System.out.println(Thread.currentThread().getName() + ",a = " + a++);

    //线程运行完毕以后要让线程A2执行,所以修改标识变量为false
    this.bshouldA1Run = false;
    //唤醒等待的单个线程
    condition.signal();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    finally
    {
    lock.unlock();
    }
    }

    public void printA2()
    {
    try {
    lock.lock();
    if(bshouldA1Run)
    condition.await();

    System.out.println(Thread.currentThread().getName() + ",a = " + a++);

    this.bshouldA1Run = true;
    condition.signal();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    finally
    {
    lock.unlock();
    }
    }
    }
      

  22.   

    建议使用winxp或者win2003的操作系统,就知道区别了
      

  23.   

    楼主思想是正确的,问题出在Integer是final的,每变一次都是一个新的实例,使用synchronized锁一个变化的Integer是肯定有问题的
      

  24.   


    Integer等8中基本数据类型 和String 都有  池 的概念。
    涉及到池就麻烦了。
    a++;之后 引用a 指向的是 对象  还是 池中的数字呢?看来,使用synchronized锁一个变化的Integer是肯定有问题的
      

  25.   

    我想:问题出在Integer上,看 33楼
    改用Dog类,就锁住了。
      

  26.   


    Integer等8中包装类型,和String类
    都有 池 的概念。当执行a++;引用指向的是原来的对象,还是 池中的数字?我们无法判断
    如果 是原来的对象(也就是 原来对象的值是增加了,并且引用a还指向那里),那么Synchronized是能锁住的。
    如果 a指向了池中的数字,(已经不是原来的对象了),那么Synchronized一点用都没有。事实也是这样!
    综述:
       用Synchronized来锁Integer等8种包装类型和String是没有意义的。另:
       有没有一种方法,能打印出 a++ 的地址(就像Object类的toString()方法一样),这样就能判断是否是原来的对象?