【Java|多线程与高并发】线程安全问题以及synchronized使用实例

这篇具有很好参考价值的文章主要介绍了【Java|多线程与高并发】线程安全问题以及synchronized使用实例。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 前言

Java多线程环境下,多个线程同时访问共享资源时可能出现的数据竞争和不一致的情况。

线程安全一直都是一个令人头疼的问题.为了解决这个问题,Java为我们提供了很多方式.

  1. synchronized关键字、ReentrantLock类等。
  2. 使用线程安全的数据结构,例如ConcurrentHashMap、ConcurrentLinkedQueue等,避免共享资源
  3. 使用volatile关键字保证内存可见性等方法。

本文主要介绍synchronized关键字
【Java|多线程与高并发】线程安全问题以及synchronized使用实例

2. 线程安全问题演示

多线程环境下可能会产生的问题
先看下面这个Test类:

class Test{
    private int count = 0;
    public void add(){
        count++;
    }

    public int getCount() {
        return count;
    }
}

count是一个普通的成员变量,提供了一个add()方法,让count进行自增
同时提供了一个get方法获取到count的值.

再来看下面这段代码:

public class Demo8 {
    private static Test test = new Test();
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                test.add();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 50000; i++) {
                test.add();
            }
        });
        t1.start();
        t2.start();

        t1.join();
        t2.join();
        System.out.println(test.getCount());
    }
}

创建了一个Test类的实例,同时创建两个线程.
让这两个线程各调用50000次Test实例的add()方法,最后输出count的值.

按理来说应该是count的值应该是100000.

运行结果:
【Java|多线程与高并发】线程安全问题以及synchronized使用实例
【Java|多线程与高并发】线程安全问题以及synchronized使用实例
可以看到运行结果不是100000,而是比100000小. 而且两次运行的结果还不一样.
这就是一个典型的线程安全问题

3.线程安全问题的原因

上述问题产生的原因:
这里的原因主要是 count++操作,count++是三条指令在CPU上执行的.
count++可以分为3个步骤:
【Java|多线程与高并发】线程安全问题以及synchronized使用实例

  1. 由于当前是两个线程同时修改同一份变量
  2. 且修改操作不是原子的
  3. 加上线程之间调度的不确定性

上述三条原因导致了这两个线程执行count++操作时,执行的顺序会有多种情况.
例如:
【Java|多线程与高并发】线程安全问题以及synchronized使用实例
ps: 这只是其中的一种情况

如果是这种情况会导致什么呢?

看这张图:
【Java|多线程与高并发】线程安全问题以及synchronized使用实例
t2线程已经修改过count的值了,但是由于t1并没有获取到count最新的值,因此t1只是在原来读到的count值得基础上进行修改.

因此虽然是执行了两次count++操作,但是真正执行完的效果只有一次count++.这也就是为什么count的值小于100000的原因了.

如果想要让执行完的结果为100000,那么就需要使用synchronized关键字进行加锁了

4.synchronized关键字

synchronized关键字是用于实现线程同步的机制,可以保证在同一时刻只有一个线程可以访问被synchronized包围的代码块或方法,从而避免了多个线程同时访问共享资源时可能出现的数据竞争和不一致的情况。

以上是一些synchronized关键字的介绍,相比大家也不喜欢看这些定义.

接下来就以我自己的方式为大家讲解:

上述问题的解决:
使用synchronizedcount++操作进行加锁,count++之后再进行解锁

此时一个线程在count++时进行加锁后,另外一个线程想进行修改,是修改不了的.这个线程只能阻塞等待

上面提到了使用synchronized进行加锁,什么是加锁呢?
例如下面这张图:
【Java|多线程与高并发】线程安全问题以及synchronized使用实例
之前疫情期间,相比大家核酸都没少做吧.
做核酸一次只能给一个人做,如有当前已经有人在做核酸了,那么下一个人只能等待.只有等前面那个人做完,才能够去做.

这里的做核酸就相当于 count++, 去做核酸就相当于是加锁, 在有人做核酸时,其它人只能等待(阻塞等待).做完离开,就相当于解锁(释放锁)

那么如果使用synchronized进行加锁解决上面的问题呢? .

可以对add方法进行加锁.
【Java|多线程与高并发】线程安全问题以及synchronized使用实例
执行add方法时会加锁,执行完add方法会解锁.

修改之后的运行结果就是100000了.
【Java|多线程与高并发】线程安全问题以及synchronized使用实例

synchronized关键字可以应用于方法(上述示例)和代码块两种情况:

  1. 修饰方法:将synchronized关键字放在方法声明前,表示该方法是同步方法,只有一个线程可以访问该方法。例如:
public synchronized void method() {
    // 同步代码块
}
  1. 修饰代码块:将synchronized关键字放在代码块前,表示该代码块是同步代码块,只有一个线程可以执行该代码块。例如:
public void method() {
    synchronized (锁对象) {
        // 同步代码块
    }
}

在Java中任何对象都可以作为"锁对象".这里的锁对象十分关键

两个(多个)线程针对同一个对象进行加锁,才会有锁竞争. 如果不对同一个对象进行加锁,每个线程各执行各的,就不会产生锁竞争.也就不会阻塞等待.

因此使用synchronized的加锁的时候,要考虑清楚对哪段代码进行加锁,锁的代码不同,对执行的效果会有很大的影响.

锁的代码越多,锁的粒度越大/越粗,所得代码越少,锁的粒度越小/越细

synchronized关键字的使用需要注意以下几点:

  1. synchronized关键字只能保证同一个对象的同步代码块或同步方法是互斥的,不同对象的同步代码块或同步方法是不互斥的。
  2. synchronized关键字的使用会降低程序的执行效率,因为它会导致线程的上下文切换和锁的竞争。
  3. synchronized关键字只能保证互斥的访问,不能保证线程安全,需要结合其他机制来保证线程安全,例如使用volatile关键字、Atomic类、Lock接口等。

5. 总结

synchronized关键字是Java中实现线程同步的重要机制之一,虽然它的使用会带来一定的性能开销,但是在多线程并发访问共享资源时,使用synchronized关键字可以有效地避免数据竞争和不一致的情况,保证程序的正确性和稳定性。
【Java|多线程与高并发】线程安全问题以及synchronized使用实例

感谢你的观看!希望这篇文章能帮到你!
专栏: 《从零开始的Java学习之旅》在不断更新中,欢迎订阅!
“愿与君共勉,携手共进!”
【Java|多线程与高并发】线程安全问题以及synchronized使用实例文章来源地址https://www.toymoban.com/news/detail-483477.html

到了这里,关于【Java|多线程与高并发】线程安全问题以及synchronized使用实例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java|多线程与高并发】JUC中常用的类和接口

    JUC是Java并发编程中的一个重要模块,全称为 Java Util Concurrent (Java并发工具包),它提供了一组用于多线程编程的工具类和框架,帮助开发者更方便地编写线程安全的并发代码。 本文主要介绍 Java Util Concurrent 下的一些常用接口和类 Callable接口类似于Runnable. 有一点区别就是

    2024年02月12日
    浏览(22)
  • 【并发多线程】java并发中的Synchronized关键词

    如果在多线程的环境中,我们经常会遇到资源竞争的情况,比如多个线程要去同时修改同一个共享变量,这时候,就需要对资源的访问方法进行一定的处理,保证同一时间只有一个线程访问。 java提供了synchronized,方便我们实现上述操作。 我们举个例子,我们创建一个

    2023年04月13日
    浏览(32)
  • Java并发编程(三)线程同步 上[synchronized/volatile]

    当使用多个线程来访问同一个数据时,将会导致数据不准确,相互之间产生冲突,非常容易出现线程安全问题,比如多个线程都在操作同一数据,都打算修改商品库存,这样就会导致数据不一致的问题。 所以我们通过线程同步机制来保证线程安全,加入同步锁以避免在该线程没有完成

    2024年02月13日
    浏览(26)
  • 【Java|多线程与高并发】设计模式-单例模式(饿汉式,懒汉式和静态内部类)

    设计模式是一种在软件开发中常用的解决复杂问题的方法论。它提供了一套经过验证的解决方案,用于解决特定类型问题的设计和实现。设计模式可以帮助开发人员提高代码的可重用性、可维护性和可扩展性。 设计模式有很多,本文主要介绍单例模式. 单例模式是一种创建型设

    2024年02月11日
    浏览(41)
  • JUC并发编程-线程和进程、Synchronized 和 Lock、生产者和消费者问题

    源码 + 官方文档 面试高频问! java.util 工具包、包、分类 业务:普通的线程代码 Thread Runnable Runnable 没有返回值、效率相比入 Callable 相对较低! 线程、进程,如果不能使用一句话说出来的技术,不扎实! 进程:一个程序,QQ.exe Music.exe 程序的集合; 一个进程往往可以包含多

    2024年01月20日
    浏览(37)
  • 多线程与高并发--------线程池

    在开发中,为了提升效率的操作,我们需要将一些业务采用多线程的方式去执行。 比如有一个比较大的任务,可以将任务分成几块,分别交给几个线程去执行,最终做一个汇总就可以了。 比如做业务操作时,需要发送短信或者是发送邮件,这种操作也可以基于异步的方式完

    2024年02月13日
    浏览(27)
  • 多线程与高并发——并发编程(4)

    1.1 生产者消费者概念 生产者-消费者是设计模式的一种,让生产者和消费者基于一个容器来解决强耦合的问题。生产者与消费者彼此之间不会直接通讯,而是通过一个容器(队列)进行通讯。 生产者生产完数据后扔到容器中,不用等消费者来处理; 消费者也不需要去找生产

    2024年02月10日
    浏览(31)
  • 多线程与高并发——并发编程(5)

    为什么要使用线程池? 在开发中,为了提升效率,我们需要将一些业务采用多线程的方式去执行。比如,有一个比较大的任务,可以将任务分成几块,分别交给几个线程去执行,最终做一个汇总即可。再比如,做业务操作时,需要发送短信或邮件,这些操作也可以基于异步的

    2024年02月09日
    浏览(32)
  • 多线程与高并发--------阻塞队列

    1.1 生产者消费者概念 生产者消费者是设计模式的一种。让生产者和消费者基于一个容器来解决强耦合问题。 生产者 消费者彼此之间不会直接通讯的,而是通过一个容器(队列)进行通讯。 所以生产者生产完数据后扔到容器中,不通用等待消费者来处理。 消费者不需要去找

    2024年02月13日
    浏览(27)
  • 【JavaEE】多线程之线程安全(synchronized篇),死锁问题

    线程安全问题 观察线程不安全 线程安全问题的原因  从原子性入手解决线程安全问题 ——synchronized synchronized的使用方法  synchronized的互斥性和可重入性 死锁 死锁的三个典型情况  死锁的四个必要条件  破除死锁 在前面的章节中,我们也了解到多线程为我们的程序带来了

    2024年02月01日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包