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);

        }

    }

     

    运行结果:

  • 相关阅读:
    很漂亮的按钮css样式(没有用到图片,可直接拷贝代码使用)
    if、while中变量的作用域问题
    笔记
    搭建高可用mongodb集群(一)——配置mongodb
    Java编程:删除 List 元素的三种正确方法
    MySQL 数据类型
    MySQL 通用查询日志(General Query Log)
    mysql 创建一个用户,指定一个数据库
    MySQL 5.7 免安装版配置
    String,StringBuffer与StringBuilder的区别??
  • 原文地址:https://www.cnblogs.com/grimm/p/6732603.html
Copyright © 2011-2022 走看看