JavaEE初阶——线程安全

目录

一.引言

二.线程安全

2.1  什么是线程安全?

2.2 线程不安全的原因

1的解释:

2的解释:

3的解释:

4的解释:

三.如何解决线程不安全

3.1  synchronized关键字

​编辑 3.2  synchronized关键字特性

1.互斥

2.可重入

3.3  synchronized关键字使用操作

1.修饰代码块

锁任意对象

2.锁当前对象 

2.直接修饰普通方法

3.修饰静态方法 

四.Java标准库中的线程安全类

五.总结


一.引言

  在Java中,线程安全是十分重要的。因为我们要让代码编写出来后锁产生的结果与我们的想法是一致的。所以本篇文章就为大家讲解一下线程安全的相关内容。

二.线程安全

2.1  什么是线程安全?

  如果多线程环境下代码运行的结果是符合我们的预期的,即在单线程环境下应该获得的结果,则说这个程序是线程安全的。

2.2 线程不安全的原因

1.线程的调度是随机的,抢占式执行

2.当前代码,多个线程同时修改一个变量

3.线程对于变量的修改不是“原子”

4.内存可见性问题

5.指令重排序问题(4,5之后会学习到,这里就暂且不讨论)

1的解释:

在程序运行的过程中,如果不加其他代码,计算机是不会知道哪个线程先进行,哪个后进行,执行的顺序是随机的,这就是抢占式执行。

2的解释:

对于一个变量例如count,线程1对其++,线程2也对其++,这样会导致返回值出错

public class Demo6 {
    private volatile static int count=0;

    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(() ->{
            for (int i = 0; i < 50000; i++) {
                count++;
            }
        });

        Thread t2=new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                count++;
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("count="+count);
    }
}

按理说结果应该是100000,但执行后结果: 

这就是多个线程改变同一个变量的结果,造成的线程不安全。

3的解释:

对于n++其实是三步操作:

1.从内存把数据读到CPU

2.进行数据更新

3.将更新的数据写回到CPU

原子性就是说当一个线程在执行的过程中,其他线程不能中途插进来。

4的解释:

假设有以下三件事要做:

1.去楼下拿快递

2.去客厅接杯水

3.去楼下拿外卖

如果按照1-2-3执行,效率会变低,所以JVM可以优化此操作,变为1-3-2,少去一次楼下,这就是指令重排序。

编译器对于指令重排序的前提是 "保持逻辑不发⽣变化". 这⼀点在单线程环境下⽐较容易判断, 但是
在多线程环境下就没那么容易了, 多线程的代码执⾏复杂程度更⾼, 编译器很难在编译阶段对代码的
执⾏效果进⾏预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价.

三.如何解决线程不安全

3.1  synchronized关键字

synchronized的作用就是加锁,解锁

上面count++的例子,如果加上了锁,那么结果就是正确的。

public class Demo6 {
    private volatile static int count=0;

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

        Object object=new Object();

        Thread t1=new Thread(() ->{
            synchronized (object){
                for (int i = 0; i < 50000; i++) {
                    count++;
                }
            }
        });

        Thread t2=new Thread(()->{
            synchronized (object){
                for (int i = 0; i < 50000; i++) {
                    count++;
                }
            }
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("count="+count);
    }
}

代码结果:

 3.2  synchronized关键字特性

1.互斥

synchronized会起到互斥的效果,某个线程执行到某个对象的synchronized中时,其他线程如果也执行到同一个对象synchronized就会阻塞等待。

进入synchronized修饰的代码块,相当于加锁。

推出synchronized修饰的代码块,相当于解锁。

“阻塞等待”的定义:

针对每⼀把锁, 操作系统内部都维护了⼀个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝试进⾏加锁, 就加不上了, 就会阻塞等待, ⼀直等到之前的线程解锁之后, 由操作系统唤醒⼀个新的线程, 再来获取到这个锁。

注意:

假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C 都在阻塞队列中排队等待. 但是当 A 释放锁之后, 虽然 B ⽐ C 先来的, 但是 B 不⼀定就能获取到锁, ⽽是和 C 重新竞争, 并不遵守先来后到的规则.

2.可重入

synchronized同步块对同一线程来说是可重入的。

不可重入是什么意思?

简单来说就是一个线程已经加上锁了,但是它又加上了同一个锁,它既释放锁,又要再一次加锁,就导致成为了死锁,但synchronized没有这样的问题,所以它是可重入锁。

3.3  synchronized关键字使用操作

1.修饰代码块

锁任意对象
public class Demo7 {
    Object object=new Object();

    public void method(){
        synchronized (object){
            
        }
    }
}
2.锁当前对象 
public class Demo8 {
    public void method(){
        synchronized (this){
            
        }
    }
}

2.直接修饰普通方法

public class Demo9 {
    public synchronized void method(){
        
    }
}

3.修饰静态方法 

public class Demo10 {
    public synchronized static void method(){
        
    }
}

四.Java标准库中的线程安全类

 Java 标准库中很多都是线程不安全的. 这些类可能会涉及到多线程修改共享数据, ⼜没有任何加锁措施。

不安全 如:ArrayList,LinkedList,HashMap,TreeMap,HashSet,TreeSet,StirngBuilder

安全 如:Vector,HashTable,ConcurrentHashMap,StringBuffer(都涉及带锁操作)

虽然存在没有加锁的,但是不涉及“修改”,仍然是线程安全的:

String

五.总结

本篇文章简单地给大家介绍了线程安全的意义,线程不安全的原因,以及其中的一种解决方法,欢迎大家在评论区讨论,感谢大家观看!

  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!对于JavaEE开发,Eclipse是一个非常流行的集成开发环境(IDE)。它为JavaEE开发者提供了丰富的功能和工具,使他们能够更轻松地构建和管理JavaEE应用程序。 在Eclipse中进行JavaEE开发,您可以使用Eclipse的JavaEE插件(通常称为Eclipse Enterprise Edition for JavaEE),它包含了一系列用于JavaEE开发的工具和模块。这些工具包括Servlet容器、JSP编辑器、EJB开发工具、数据库连接工具等等。 使用Eclipse进行JavaEE开发的步骤如下: 1. 安装Eclipse:您可以从Eclipse官方网站下载适合您操作系统的Eclipse版本,并按照安装指南进行安装。 2. 安装JavaEE插件:在Eclipse中,通过插件安装向导安装JavaEE插件。您可以在Eclipse的市场中搜索并安装适合您版本的JavaEE插件。 3. 创建JavaEE项目:在Eclipse中,可以通过菜单或快捷键创建新的JavaEE项目。选择适当的项目类型(如Dynamic Web Project、EJB Project等),并按照向导进行项目配置。 4. 开发和调试代码:在Eclipse中,您可以使用内置的编辑器编写JavaEE代码,使用调试器进行调试,并利用各种工具和视图来构建和测试您的应用程序。 5. 部署和运行应用程序:完成代码的开发和调试后,您可以将您的JavaEE应用程序部署到适当的服务器上,并启动服务器以运行您的应用程序。 当然,这只是一个简要的介绍。对于JavaEE开发,Eclipse还提供了许多其他功能和工具,如代码自动完成、版本控制、构建工具集成等等。 希望这能帮助到您!如果您还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值