多线程——同步问题
我们先看个错误示例。代码功能:声明一个数字并赋值10000.然后让1w个线程去减少1,1w个线程去增加1。理论上说,加一万减一万,最后数字的结果并不会改变。
代码:
class ErrorDemo{ private static int Num=10000; public static void main(String[] args) throws InterruptedException { int n=10000; //声明线程数组,在后面使用join让主线程等待所有线程执行完。 // 不然主线程跑完了,其他线程没执行完就输出结果会不对的。 Thread[] add=new Thread[n]; Thread[] reduce=new Thread[n]; //创建1w个减少线程 for(int i=0;i<n;i++){ Thread t1=new Thread(){ public void run(){ Num-=1; } }; t1.start(); reduce[i]=t1; } //创建1w个增加线程 for(int i=0;i<n;i++){ Thread t2=new Thread(){ public void run(){ Num+=1; } }; t2.start(); add[i]=t2; } //让主线程等待所有线程执行完毕 for(Thread t:add) { t.join(); } for(Thread t:reduce) { t.join(); } //输出结果 System.out.println(Num); } }
最后输出的结果是9999。这个结果不固定,有多有少。
造成这种错误的原因是:
假设增加线程获取到数字是10000,进行了加一操作,结果是10001。
但是减少线程在返回结果之前也获取到了数字10000。
减少线程最后返回9999。
所以我们原本期望得到的10000变成了9999。
为了解决这种问题,可以使用synchronized
使用方法:
Object object=new Object();//object就是你当前线程操作的对象,比如上面的int数字 synchronized (object){ //当前线程独占了object,其他线程访问object就会等待当前线程释放object }
释放object的方法:
synchronized代码块结束或者异常抛出。
使用synchronized后的代码
public class Thread_synchronization { } class ErrorDemo{ public static void main(String[] args) throws InterruptedException { //因为synchronized里面要求的是对象,所以需要用Integer声明 Integer Num = 10000; int n=10000; //声明线程数组,在后面使用join让主线程等待所有线程执行完。 // 不然主线程跑完了,其他线程没执行完就输出结果会不对的。 Thread[] add=new Thread[n]; Thread[] reduce=new Thread[n]; //创建1w个减少线程 for(int i=0;i<n;i++){ Thread t1=new Thread(){ public void run(){ synchronized (Num) { addNum(Num); } } }; t1.start(); reduce[i]=t1; } //创建1w个增加线程 for(int i=0;i<n;i++){ Thread t2=new Thread(){ public void run(){ synchronized (Num) { reduceNum(Num); } } }; t2.start(); add[i]=t2; } //让主线程等待所有线程执行完毕 for(Thread t:add) { t.join(); } for(Thread t:reduce) { t.join(); } //输出结果 System.out.println(Num); } public static int addNum(int Num){ return Num+1; } public static int reduceNum(int Num){ return Num-1; } }
最后的结果是10000
除了上面的方法,还可以直接在操作数据的方法前加上synchronized
public class Thread_synchronization { } class ErrorDemo{ public static void main(String[] args) throws InterruptedException { //因为synchronized里面要求的是对象,所以需要用Integer声明 Integer Num = 10000; int n=10000; //声明线程数组,在后面使用join让主线程等待所有线程执行完。 // 不然主线程跑完了,其他线程没执行完就输出结果会不对的。 Thread[] add=new Thread[n]; Thread[] reduce=new Thread[n]; //创建1w个减少线程 for(int i=0;i<n;i++){ Thread t1=new Thread(){ public void run(){ addNum(Num); } }; t1.start(); reduce[i]=t1; } //创建1w个增加线程 for(int i=0;i<n;i++){ Thread t2=new Thread(){ public void run(){ reduceNum(Num); } }; t2.start(); add[i]=t2; } //让主线程等待所有线程执行完毕 for(Thread t:add) { t.join(); } for(Thread t:reduce) { t.join(); } //输出结果 System.out.println(Num); } public synchronized static int addNum(int Num){ return Num+1; } public synchronized static int reduceNum(int Num){ return Num-1; } }
最后的结果是10000
如果一个类里面的所有方法都被synchronized修饰,那么这个类就是线程安全的类。
同一时间,只有一个线程可以进入这个类的一个实例去修改数据,以免多个线程同时修改数据,而产生脏数据。