首页 > 编程语言 >Java:从单线程计数器到多线程数据同步synchronized和原子类Atomic

Java:从单线程计数器到多线程数据同步synchronized和原子类Atomic

时间:2023-06-05 10:02:19浏览次数:48  
标签:count Java synchronized thread Thread static 10000 多线程 public

(目录)

使用单线程

单线程修改计数器的值,没有发生问题,每次运行结果都是10000,不过程序耗时较长

package com.example;

/**
 * 计数器
 */
class Counter {
    private static long count;

    public static long getCount() {
        return count;
    }

    public static void incrementCount() {
        count++;
    }
}


public class Demo {
    public static void main(String[] args) throws InterruptedException {
        long count = Counter.getCount();
        System.out.println(count);
        // 0

        for (int i = 0; i < 10000; i++) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Counter.incrementCount();
        }
        
        count = Counter.getCount();
        System.out.println(count);
        // 10000
    }
}

使用多线程

单线程修改计数器的值,运行速度提高了,不过运行结果每次都不一致,而且结果不是10000

package com.example;

import java.util.ArrayList;
import java.util.List;

/**
 * 计数器
 */
class Counter {
    private static long count;

    public static long getCount() {
        return count;
    }

    public static void incrementCount() {
        count++;
    }
}


public class Demo {
    public static void main(String[] args) throws InterruptedException {
        long count = Counter.getCount();
        System.out.println(count);
        // 0

        List<Thread> list = new ArrayList<>();

        // 启动10000个线程同时访问计数器
        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Counter.incrementCount();
                }
            });
            list.add(thread);
        }

        for (Thread thread : list) {
            thread.start();
        }

        for (Thread thread : list) {
            thread.join();
        }

        count = Counter.getCount();
        System.out.println(count);
    }
}

执行结果

第一次:9910
第二次:9912
第三次:9910

使用多线程 + synchronized

多线程加锁后,最后结果都是10000

package com.example;

import java.util.ArrayList;
import java.util.List;

/**
 * 计数器
 */
class Counter {
    private static long count;

    public static long getCount() {
        return count;
    }

    public static synchronized void incrementCount() {
        count++;
    }
}


public class Demo {
    public static void main(String[] args) throws InterruptedException {
        long count = Counter.getCount();
        System.out.println(count);
        // 0

        List<Thread> list = new ArrayList<>();

        // 启动10000个线程同时访问计数器
        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Counter.incrementCount();
                }
            });
            list.add(thread);
        }

        for (Thread thread : list) {
            thread.start();
        }

        for (Thread thread : list) {
            thread.join();
        }

        count = Counter.getCount();
        System.out.println(count);
    }
}

执行结果

第一次:10000
第二次:10000
第三次:10000

使用多线程 + 原子类AtomicLong

多线程中使用原子类AtomicLong实现计数器,最后结果都是10000

原理是CAS(Compare and Set):

  • 先比较原始值和预期值,如果相等,则修改为新值;
  • 不相等则修改失败

伪代码如下

bool compareAndSet(oldValue, expectValue, updateValue){
    if(oldValue == expectValue){
        oldValue = updateValue
        // update success
    } else{
        // update fail
    }
}
package com.example;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 计数器
 */
class Counter {
    private static AtomicLong count = new AtomicLong(0);

    public static long getCount() {
        return count.get();
    }

    public static void incrementCount() {
        count.incrementAndGet();
    }
}


public class Demo {
    public static void main(String[] args) throws InterruptedException {
        long count = Counter.getCount();
        System.out.println(count);
        // 0

        List<Thread> list = new ArrayList<>();

        // 启动10000个线程同时访问计数器
        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Counter.incrementCount();
                }
            });
            list.add(thread);
        }

        for (Thread thread : list) {
            thread.start();
        }

        for (Thread thread : list) {
            thread.join();
        }

        count = Counter.getCount();
        System.out.println(count);
    }
}

执行结果

第一次:10000
第二次:10000
第三次:10000

参考

  1. 使用Atomic-廖雪峰的官方网站
  2. CAS锁机制(无锁、自旋锁、乐观锁、轻量级锁)
  3. java中的Atomic类

标签:count,Java,synchronized,thread,Thread,static,10000,多线程,public
From: https://blog.51cto.com/mouday/6413292

相关文章

  • 基于JAVA操作系统在线网站SQL
    随着21世纪的到来,人们更深刻的感受到了计算机在生活和工作中作用的重要,越来越多的职业需要具有计算机的应用技能。掌握计算机是职业的需要,社会的需要,更是事业发展的需要。今天,计算机技术不但广泛地应用在办公自动化中,还全面参与到各行各业。所有与计算机相关的职业都要求工作者有......
  • Java学习笔记(十四)
    1.请描述你理解的IO流的作用 I/O流(输入/输出流)的作用是在程序与外部世界(例如文件、网络、控制台等)之间传输数据。2.请描述I/O流的体系结构(1)InputStream类和OutputStream类,其实现类:FileInputStream和FileOutputStream(2)Reader类和Writer类,其实现类:FileReader和FileWriter(3)缓......
  • Java Map 集合类简介
    源:http://www.oracle.com/technetwork/cn/articles/maps1-100947-zhs.html#T1评:了解最常用的集合类型之一Map的基础知识以及如何针对您应用程序特有的数据优化Map。java.util中的集合类包含Java中某些最常用的类。最常用的集合类是List和Map。......
  • 04.如何创建并运行java线程
    评:原文链接译者:章筱虎校对:方腾飞Java线程类也是一个object类,它的实例都继承自java.lang.Thread或其子类。可以用如下方式用java中创建一个线程:查看源代码打印帮助1Treadthread=newThread();执行该线程可以调用该线程的start()方法:查看源代码打印帮助1thre......
  • Java URL正则表达式
    评:网上转贴的Java正则很让人失望,一篇JavaScript正则集录被很多人当成Java正则表达式转来转去,我擦,太坑爹了吧。自己写一个吧importjava.util.regex.Matcher;importjava.util.regex.Pattern;publicclassRegularExpressionTest{publicstaticvoidmain(S......
  • 浅谈java异常[Exception]
    评:一.异常的定义在《java编程思想》中这样定义异常:阻止当前方法或作用域继续执行的问题。虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常。绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败。之所以java要提出异常处理机制,就是要......
  • 商城系统比较(三款不错的java开源商城系统推荐)和其他推荐
    今天给大家分享三套github上优质的商城开源项目,排名不分先后。1、newbee-mall(新蜂商城)仓库地址:https://github.com/newbee-ltd/newbee-mall特点:springboot开发,主要针对pc端,代码设计非常简洁,没有过多的分层,非常适合初学者学习。 2、macrozheng/mall(没有中文名,不太好记)仓库地......
  • java的异常
    异常:程序中发生的不正常的情况成为异常(语法错误和逻辑错误不是异常)。异常分为两类:1.Error:java虚拟机无法解决的严重问题,程序会崩溃,如栈溢出(stackoverflowerror)、内存不足等2.Exception:由于编程错误或外在因素导致的一般性问题,可以使用正对行的代码进行处理,如......
  • JAVA动态代理内部实现
    评:一代理设计模式代理模式为目标对象提供一种代理以控制对实际对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。为了保持行为的一致性,代理类和实际委托类通常会实现相同的接口,所以在访问者看来两者没......
  • 一文吃透Java并发高频面试题
    内容摘自我的学习网站:topjavaer.cn分享50道Java并发高频面试题。线程池线程池:一个管理线程的池子。为什么平时都是使用线程池创建线程,直接new一个线程不好吗?嗯,手动创建线程有两个缺点不受控风险频繁创建开销大为什么不受控?系统资源有限,每个人针对不同业务都可以手动......