zoukankan      html  css  js  c++  java
  • 多线程——同步问题

    我们先看个错误示例。代码功能:声明一个数字并赋值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修饰,那么这个类就是线程安全的类。

    同一时间,只有一个线程可以进入这个类的一个实例去修改数据,以免多个线程同时修改数据,而产生脏数据。

  • 相关阅读:
    理解session
    java ee后台运行原理(Servlet)
    XML:是什么?怎样工作的?可以做什么?将来的发展有会怎样?
    互联网应用与企业级应用的区别
    自我介绍
    补充第一周
    第一周代码(四则运算)
    自我介绍
    程序1:自动生成小学四则运算题目
    初读《构建之法现代软件工程》的5个疑问
  • 原文地址:https://www.cnblogs.com/lbhym/p/11726594.html
Copyright © 2011-2022 走看看