zoukankan      html  css  js  c++  java
  • Java线程同步

    一、问题产生

    假设一种场景:

    有一个静态变量num,初始值为0。现在开了个线1000程,每个线程内循环1000次,每循环对num自加1,问最后的值是大于、等于还是小于1000000?

    下面编写代码来看一下结果:

    importJava.util.concurrent.TimeUnit;

     

    public class Test implements Runnable{

        private static int num = 0;

       

        private void increaseNumber() {

           num++;

        }

     

        @Override

        public void run() {

           for(int i = 1; i <=1000; i++){

               increaseNumber();

               System.out.println("值为" + num + ",当前" + Thread.currentThread().getName());

           }

        }

     

        public static void main(String[] args) throws InterruptedException {  

           for (int i = 1; i <=1000; i++) {

               Thread thread = new Thread(new Test());

               thread.setName("线程"+i);

               thread.start();

           }

     

           try {

               // 等待全部子线程执行完毕

               TimeUnit.SECONDS.sleep(30);

           } catch(InterruptedException e) {

               e.printStackTrace();

           }

           System.out.println("num的最终值为" + num);

        }

    }

     

    运行结果:

      分析:这里可以看出,结果小于1000000。这是因为不同的线程可能同时访问num,比如两个线程同时在某个时候访问num,这个时候num只自加了1次。若是改为先后访问,则num应该自加2次。这样导致最终结果不等于1000000。

     

    二、同步

    为了防止数据被两个或多个线程同时访问,咱们可以用synchronized关键字将数据保护起来。Synchronized的内部机理是用锁对代码段进行保护,线程A要访问这段代码时,先查看数据代码段是否处于加锁状态。若代码段处于加锁状态,说明有别的线程在访问这段代码,必须等特别的进程访问完,锁才会被释放,然后A线程才可以访问这段代码。若数据未处于加锁状态,则线程A可以访问这段代码,同时给该代码段加锁,这样别的线程要访问这块代码段就得等线程A把锁释放掉(代码段执行完时释放锁)。

    同步后的代码如下:

    importjava.util.concurrent.TimeUnit;

     

    public class Test implements Runnable{

        private static int num = 0;

       

        synchronized private void increaseNumber() {

           num++;

        }

     

        @Override

        public void run() {

           for(int i = 1; i <=1000; i++){

               increaseNumber();

               System.out.println("值为" + num + ",当前" + Thread.currentThread().getName());

           }

        }

     

        public static void main(String[] args) throws InterruptedException {  

           for (int i = 1; i <=1000; i++) {

               Thread thread = new Thread(new Test());

               thread.setName("线程"+i);

               thread.start();

           }

     

           try {

               // 等待全部子线程执行完毕

               TimeUnit.SECONDS.sleep(30);

           } catch(InterruptedException e) {

               e.printStackTrace();

           }

           System.out.println("num的最终值为" + num);

        }

    }

     

    运行结果:

    分析:在increateNumber()方法加上synchronized关键字后,num的值仍然不为1000000,这是什么原因呢?这要从synchronized的作用对象来分析。事实上,synchronized的作用对象是this,也就是当前对象。这样只有多个线程访问同一对象时,synchronized才会起作用。而上面代码是生成了1000个对象,即new Test()1000次,所以synchronized加跟没加都一样。

     

    三、静态同步

    若一个类的多个对象(这个例子中是1000个对象)要访问同一个数据(这里是static int num),要考虑对整个类进行同步,这样被同步的代码块就作用于该类的所有实例,从而不会被两个或多个线程(本例中一个线程就是一个实例)同时访问。Java中,是用static synchronized关键字来表示静态同步。

    importjava.util.concurrent.TimeUnit;

     

    public class Test implements Runnable{

        private static int num = 0;

       

        static synchronized private void increaseNumber() {

           num++;

        }

     

        @Override

        public void run() {

           for(int i = 1; i <=1000; i++){

               increaseNumber();

               System.out.println("值为" + num + ",当前" + Thread.currentThread().getName());

           }

        }

     

        public static void main(String[] args) throws InterruptedException {  

           for (int i = 1; i <=1000; i++) {

               Thread thread = new Thread(new Test());

               thread.setName("线程"+i);

               thread.start();

           }

     

           try {

               // 等待全部子线程执行完毕

               TimeUnit.SECONDS.sleep(30);

           } catch(InterruptedException e) {

               e.printStackTrace();

           }

           System.out.println("num的最终值为" + num);

        }

    }

     

    运行结果:

  • 相关阅读:
    大作文-学以”成人”
    方案类--博物院整改意见
    归纳概括-我国中小学开展研学旅行活动的特点
    短文-网络新一代
    短评
    讲话稿-文明素养教育主题宣传
    检验用户单点登录方案解决
    Spring @Transactional注解
    RPC-局限于java的RMI
    Redis缓存雪崩、击穿、穿透的问题和解决方式
  • 原文地址:https://www.cnblogs.com/grimm/p/6732603.html
Copyright © 2011-2022 走看看