Java实现线程同步的四种方式总结

 更新时间:2022年09月30日 14:26:30   作者:mikechen  
Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式,需要的可以参考一下

什么是线程同步

当使用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现线程安全问题,如下图所示:

比如多个线程都在操作同一数据,都打算修改商品库存,这样就会导致数据不一致的问题。

线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

所以我们用同步机制来解决这些问题,加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。

线程同步的几种方式

1.使用synchronized关键字

这种方式比较灵活,修饰一个代码块,被修饰的代码块称为同步语句块。

其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象,如下格式:

synchronized(对象) {					//得到对象的锁,才能操作同步代码

    需要被同步代码;

}

通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

具体的示例如下:

public class SynchronizedThread {

    class Bank {

        private int account = 200;

        public int getAccount() {

            return account;

        }

        /**
         * 用同步方法实现
         *
         * @param money
         */

        public synchronized void save(int money) {
            account += money;

        }

        /**
         * 用同步代码块实现
         *
         * @param money
         */

        public void save1(int money) {
            synchronized (this) {

                account += money;
           }

        }
    }


    class NewThread implements Runnable {
        private Bank bank;

        public NewThread(Bank bank) {

            this.bank = bank;

        }


        @Override
        public void run() {

            for (int i = 0; i < 10; i++) {
                // bank.save1(10);
                bank.save(10);
                System.out.println(i + "账户余额为:" + bank.getAccount());

            }

        }

    }


    /**
     * 建立线程,调用内部类
     */

    public void useThread() {
        Bank bank = new Bank();
        NewThread new_thread = new NewThread(bank);
        System.out.println("线程1");

        Thread thread1 = new Thread(new_thread);
        thread1.start();
        System.out.println("线程2");
        Thread thread2 = new Thread(new_thread);
        thread2.start();

    }

    public static void main(String[] args) {

        SynchronizedThread st = new SynchronizedThread();

        st.useThread();

    }
}

如果你还想深入了解Synchronized的底层原理,可以看 Synchronized的实现原理详解(看这篇就够了)

2.使用ReentrantLock

ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法具有相同的基本行为和语义,并且扩展了其能力。

 

private int account = 100;

            //需要声明这个锁

            private Lock lock = new ReentrantLock();

            public int getAccount() {

                return account;

            }

            //这里不再需要synchronized

            public void save(int money) {

                lock.lock();

                try{

                    account += money;

                }finally{

                    lock.unlock();

                }

            }

        }

synchronized 与 Lock 的对比

ReentrantLock是显示锁,手动开启和关闭锁,别忘记关闭锁;

synchronized 是隐式锁,出了作用域自动释放;

ReentrantLock只有代码块锁,synchronized 有代码块锁和方法锁;

使用 ReentrantLock锁,JVM 将花费较少的时间来调度线程,线程更好,并且具有更好的扩展性(提供更多的子类);

优先使用顺序:

ReentrantLock> synchronized 同步代码块> synchronized 同步方法

3.使用原子变量实现线程同步

为了完成线程同步,我们将使用原子变量(Atomic***开头的)来实现。

比如典型代表:AtomicInteger类存在于java.util.concurrent.atomic中,该类表示支持原子操作的整数,采用getAndIncrement方法以原子方法将当前的值递加。

具体示例如下:

private AtomicInteger account = new AtomicInteger(100);

        public AtomicInteger getAccount() {

            return account;

        }

        public void save(int money) {

            account.addAndGet(money);

        }

4.ThreadLocal实现线程同步

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响,从而实现线程同步。

具体代码示例如下:

//只改Bank类,其余代码与上同

        public class Bank{
            // 创建一个线程本地变量 ThreadLocal

            private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
                @Override

                //返回当前线程的"初始值"

                protected Integer initialValue(){

                    return 100;

                }

            };

            public void save(int money){

                //设置线程副本中的值

                account.set(account.get()+money);

            }

            public int getAccount(){

                //返回线程副本中的值

                return account.get();

            }

        }

到此这篇关于Java实现线程同步的四种方式总结的文章就介绍到这了,更多相关Java线程同步内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • log4j使用教程详解(怎么使用log4j2)

    log4j使用教程详解(怎么使用log4j2)

    Log4j 2的好处就不和大家说了,如果你搜了2,说明你对他已经有一定的了解,并且想用它,所以这里直接就上手了
    2013-12-12
  • Java中的Kotlin 内部类原理

    Java中的Kotlin 内部类原理

    这篇文章主要介绍了Java中的Kotlin 内部类原理,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-06-06
  • JavaWeb基础教程之Java基础加强版

    JavaWeb基础教程之Java基础加强版

    这篇文章主要介绍了JavaWeb基础教程之Java基础加强版的相关资料,需要的朋友可以参考下
    2016-07-07
  • JAVA+Struts2获取服务器地址的方法

    JAVA+Struts2获取服务器地址的方法

    这篇文章主要介绍了JAVA+Struts2获取服务器地址的方法,是Struts2的一个简单应用,具有一定的借鉴与参考价值,需要的朋友可以参考下
    2014-11-11
  • spring boot Slf4j日志框架的体系结构详解

    spring boot Slf4j日志框架的体系结构详解

    在项目开发中记录日志是必做的一件事情,springboot内置了slf4j日志框架,下面这篇文章主要给大家介绍了关于spring boot Slf4j日志框架的体系结构,需要的朋友可以参考下
    2022-05-05
  • Spring boot2X负载均衡和反向代理实现过程解析

    Spring boot2X负载均衡和反向代理实现过程解析

    这篇文章主要介绍了Spring boot2X负载均衡和反向代理实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • 扒一扒 Java 中的枚举类型

    扒一扒 Java 中的枚举类型

    这篇文章主要给大家介绍了Java中枚举类型的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • Java利用Spire.XLS for Java实现在Excel文件中添加或删除分节符

    Java利用Spire.XLS for Java实现在Excel文件中添加或删除分节符

    在复杂的Excel报表中,合理设置分节符能有效提升文档结构与可读性,尤其在打印输出时效果显著,本文将深入探讨如何利用Spire.XLS for Java轻松实现Excel分节符的添加与删除,希望对大家有所帮助
    2025-11-11
  • Java中的抽象工厂模式_动力节点Java学院整理

    Java中的抽象工厂模式_动力节点Java学院整理

    抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。下面通过本文给大家分享Java中的抽象工厂模式,感兴趣的朋友一起看看吧
    2017-08-08
  • 详解Java List中五种常见实现类的使用

    详解Java List中五种常见实现类的使用

    Java中提供了非常多的使用的List实现类,本文将重点介绍一下常见的五种实现类以及他们的应用场景,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-10-10

最新评论