synchronized 加锁Integer对象后,多线程还是会得到相同的数据

 public class ThreadDemo implements Runnable  {

    public static volatile Integer a = 0;


    @Override
    public void run()  {

        try {
            while (a < 1000) {
                synchronized (a) {
                    a++;
                    System.out.println(Thread.currentThread() + ":"+ a);
                }
            }
        }catch (Exception e){}
    }
}

图片说明

应该正常吧,19之后是21,两个线程各加了1

synchronized (ThreadDemo .class)

不应该是外部函数创建多个线程后,线程内加锁add代码块吗,线程内部打印是什么鬼

public static volatile Integer a = 0;不要在ThreadDemo中创建声明

第一点,你a++,a对象变了,锁失效

第二点,a++不是原子操作

改成以下代码试试。

public class ThreadDemo implements Runnable {

public static volatile Integer a = 0;


@Override
public void run()  {

        try {
            for(int i=0;i<1000;i++){
            synchronized (a) {
                a++;
                System.out.println(Thread.currentThread() + ":"+ a);
            }
        }
    }catch (Exception e){}
}

}

这个是正常的,因为你的锁是a,而加锁对象是a+1,当线程1得到钥匙19后,执行a+1得到a=20,但是a++先赋值后自增,在打印a,线程1完成;线程0得到a=20,开锁,执行代码,也是打印a;两个线程在逻辑上是分开的;但是程序执行需要时间,打印a只是引用对象锁定,而对象的值未锁定,所以会出现上面的情况

http://gao-xianglong.iteye.com/blog/2396071 貌似这里的讲解可以很好解释这个问题!

a++不是原子操作,Integer a 换成 AmoticInteger a, a ++ 换成 a.getAndIncrement()

试试这段代码

 package stackoverflow;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NumberEx {
    public static List<Integer> list=new ArrayList<Integer>();
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ExecutorService service=Executors.newCachedThreadPool();
        for(int i=0;i<10;i++) {
            service.execute(new ThreadDemo(i));
        }
        service.shutdown();
        /**
        int flag=0;
        for(int i=0;i<1000;i++) {
            if(flag<10) {
                System.out.print(list.get(i)+" ");
                flag++;
            }else {
                flag=0;
                System.out.println();
                System.out.print(list.get(i)+" ");
            }
        }**/
    }
}

 package stackoverflow;

public class ThreadDemo implements Runnable  {
    public static volatile Integer a = 0;

    public int pid;
    public ThreadDemo(int id) {
        this.pid=id;
    }

    @Override
    public void run()  {

        try {
            while (a < 1000) {
                //临界区开始
                synchronized (a) {
                        System.out.print("<<<<====================================");
                        a++;
                        System.out.print(" Thread["+this.getPid()+"]" + ":"+ a);
                        System.out.println("      >>>>>====================================");  
                }
                //临界区结束
                NumberEx.list.add(a);
            }
        }catch (Exception e){}
    }

    public int getPid() {
        return pid;
    }

}

很明显,临界区根本没加锁

好几天啊,终于搞定了,首先线程共享资源必须是私有的,其次,获得对象锁加锁。
你可以把int换成atomicInteger,在public void run中对这个对象进行加锁,或者,看这个代码:

首先构造一个线程池

 package stackoverflow;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class NumberEx {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ExecutorService service=Executors.newCachedThreadPool();
        for(int i=0;i<10;i++) {
            service.execute(new ThreadDemo());
        }
        service.shutdown();

    }
}

然后我调试的时候加了一个辅助对象,list

 package stackoverflow;

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadDemo implements Runnable  {
    private static volatile int a = 0;
    private static volatile List<Integer> list=new ArrayList<Integer>();
    static PrintWriter writer;
    static {
        try {
            writer=new PrintWriter("./src/out.txt");
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }



    @Override
    public void run()  {
         synchronized(list) {
             while (a< 1000) {
                //临界区开始

                a= increment(a);
                //临界区结束

             }
         }
            writer.close();

    }

    private static synchronized int increment(int a)  {




        writer.print("<<<<============"+Thread.currentThread().getName()+"========================     ");

        a=a+1;
        Integer temp=new Integer(a);

       writer.print(" Thread["+Thread.currentThread().getName()+"]" + ":"+ a);
       writer.print("      ============="+Thread.currentThread().getName()+"=======================>>>>>    "); 
        if(list.contains(temp)) {
            System.out.println("C重复数字"+temp);
            Iterator<Integer> iter=list.iterator();
            while(iter.hasNext()) {
                System.out.print(iter.next()+" ");
            }
            System.exit(0);
        }else {
            list.add(temp);
        }
       writer.println(" "+temp+"\n");


        return a;
    } 



}

在eclipse,src下建个src文件,你会看到线程安全的输出,嘿嘿