2024年最全Java多线程(三):线程安全问题与解决方法,大厂架构师经验分享

还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!

王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。

对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!

【完整版领取方式在文末!!】

93道网络安全面试题

内容实在太多,不一一截图了

黑客学习资源推荐

最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

😝朋友们如果有需要的话,可以联系领取~

1️⃣零基础入门
① 学习路线

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

image

② 路线对应学习视频

同时每个成长路线对应的板块都有配套的视频提供:

image-20231025112050764

2️⃣视频配套工具&国内外网安书籍、文档
① 工具

② 视频

image1

③ 书籍

image2

资源较为敏感,未展示全面,需要的最下面获取

在这里插入图片描述在这里插入图片描述

② 简历模板

在这里插入图片描述

因篇幅有限,资料较为敏感仅展示部分资料,添加上方即可获取👆

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

}

}




 运行结果:



![](https://img-blog.csdnimg.cn/bce284940af64de09b59418cffd517f0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5r6E55m95piT,size_20,color_FFFFFF,t_70,g_se,x_16)



可以看到,线程2将flag修改为false,线程1始终未结束执行,这就是内存可见性问题。



2.5 指令重排序

---------



什么是指令重排序?



比如一段代码是这样的:



1.  去前台取下 U 盘

2.  去教室写 10 分钟作业

3.  去前台取下快递



        如果是在单线程情况下,JVM、CPU指令集会对其进行优化,比如,按 1->3->2的方式执行,也是没问题,可以少跑一次前台。这种叫做**指令重排序 。**编译器优化的本质是调整代码的执行顺序,在单线程下没问题,但在多线程下容易出现混乱,从而造成线程安全问题。 



那么有这么多线程不安全问题,该如何解决呢?



![](https://img-blog.csdnimg.cn/img_convert/a2c40b03f0dd0d5e67d625c91f0d12ba.png)



3\. 解决线程不安全问题

=============



3.1 volatile 解决内存可见性和指令重排序问题

----------------------------



        volatile 可以解决**内存可见性**和**指令重排序**的问题,代码在**写入 volatile 修饰的变量**的时候: 



*   改变线程⼯作内存中volatile变量副本的值;

*   将改变后的副本的值从⼯作内存刷新到主内存。



代码在**读取 volatile 修饰的变量**的时候:



*   从主内存中读取volatile变量的最新值到线程的⼯作内存中;

*   从⼯作内存中读取volatile变量的副本。



注意 :直接访问工作内存(实际是 CPU 的寄存器或者 CPU 的缓存), 速度非常快, 但是可能出现数据不⼀致的情况,加上 volatile ,强制读写内存,速度虽然慢了,但是数据变得更准确了。



volatile 演示:



public class ThreadDemo17 {

private volatile static boolean flag = true;



public static void main(String[] args) {

    Thread thread1 = new Thread(() -> {

        System.out.println("线程1:开始执行" + LocalDateTime.now());

        while (flag) {

        }

        System.out.println("线程1:结束执行" + LocalDateTime.now());

    });

    thread1.start();



    Thread thread2 = new Thread(() -> {

        try {

            Thread.sleep(1000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println("线程2:修改flag = false" + LocalDateTime.now());

        flag = false;

    });

    thread2.start();



}

}




运行结果:



![](https://img-blog.csdnimg.cn/52c358bd8c8e4d6cbca6a13f877179b8.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5r6E55m95piT,size_20,color_FFFFFF,t_70,g_se,x_16)



 给之前的代码加上 volatile 之后,线程1接收到了flag的改变,从而结束了执行,解决了内存可见性问题。



**volatile 缺点** :



        volatile 虽然可以解决内存可见性和指令重排序的问题,但是解决不了原子性问题,因此对于 ++ 和 --操作的线程非安全问题依然解决不了,比如以下代码:



public class ThreadDemoVolatile {

static class Counter {

    // 变量

    private volatile int number = 0;



    // 循环次数

    private final int MAX_COUNT;



    public Counter(int MAX_COUNT) {

        this.MAX_COUNT = MAX_COUNT;

    }



    // ++ 方法

    public void increase() {

        for (int i = 0; i < MAX_COUNT; i++) {

            number++;

        }

    }



    // -- 方法

    public void desc() {

        for (int i = 0; i < MAX_COUNT; i++) {

            number--;

        }

    }



    public int getNumber() {

        return number;

    }

}



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

    Counter counter = new Counter(100000);

    Thread thread1 = new Thread(counter::increase);

    thread1.start();

    Thread thread2 = new Thread(counter::desc);

    thread2.start();



    // 等待线程执行完成

    thread1.join();

    thread2.join();

    System.out.println("最终结果:" + counter.getNumber());

}

}




3.2 锁(synchronized 和 lock)

--------------------------



### 3.2.1 synchronized



**synchronized 基本用法:**



1.  修饰静态方法  

     ```

    public class ThreadSynchronized {

    

        private static int number = 0;

    

        static class Counter {

            // 循环次数

            private static final int count = 100000;

    

            // ++方法

            public synchronized static void increase() {

                for (int i = 0; i < count; i++) {

                    number++;

                }

            }

    

            // --方法

            public synchronized static void desc() {

                for (int i = 0; i < count; i++) {

                    number--;

                }

            }

        }

    

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

            Thread thread1 = new Thread(Counter::increase);

            thread1.start();

            Thread thread2 = new Thread(Counter::desc);

            thread2.start();

    

            // 等待线程执行完毕

            thread1.join();

            thread2.join();

            System.out.println("执行结果:" + number);

        }

    }

    ```

    

    ![](https://img-blog.csdnimg.cn/463151a13cbf4015b9849b9010c2b1f9.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5r6E55m95piT,size_20,color_FFFFFF,t_70,g_se,x_16)

    

2.  修饰普通⽅法  

     ```

    public class ThreadSynchronized2 {

        private static int number = 0;

    

        static class Counter {

            private static final int count = 100000;

    

            // ++方法

            public synchronized void increase() {

                for (int i = 0; i < count; i++) {

                    number++;

                }

            }

    

            // --方法

            public synchronized void desc() {

                for (int i = 0; i < count; i++) {

                    number--;

                }

            }

        }

    

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

            Counter counter = new Counter();

            Thread thread1 = new Thread(counter::increase);

            thread1.start();

            Thread thread2 = new Thread(counter::desc);

            thread2.start();

    

            // 等待线程执行完毕

            thread1.join();

            thread2.join();

            System.out.println("最终结果:" + number);

        }

    }

    ```

    

    ![](https://img-blog.csdnimg.cn/82fd04de1aaa4eb8a96aeddb4cfedd50.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5r6E55m95piT,size_19,color_FFFFFF,t_70,g_se,x_16)

    

3.  修饰代码块  

     ```

    public class ThreadSynchronized3 {

        private static int number = 0;

    

        static class Counter {

            private static final int count = 100000;

    

            // 自定义锁对象

            final Object myLock = new Object();

    

            // ++方法

            public void increase() {

                for (int i = 0; i < count; i++) {

                    synchronized (myLock) {

                        number++;

                    }

                }

            }

    

            // --方法

            public void desc() {

                for (int i = 0; i < count; i++) {

                    synchronized (myLock) {

                        number--;

                    }

                }

            }

        }

    

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

            Counter counter = new Counter();

            Thread thread1 = new Thread(counter::increase);

            thread1.start();

            Thread thread2 = new Thread(counter::desc);

            thread2.start();

    

            thread1.join();

            thread2.join();

            System.out.println("最终结果:" + number);

        }

    }

    ```

    ![](https://img-blog.csdnimg.cn/665b79d80ab44e0cb24bbd186d43a8ab.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5r6E55m95piT,size_19,color_FFFFFF,t_70,g_se,x_16)

    



 **synchronized 特性:**



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



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

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



**2\. 刷新内存。**synchronized 的⼯作过程: 



1.  获得互斥锁

2.  从主内存拷贝变量的最新副本到⼯作的内存

3.  执行代码

4.  将更改后的共享变量的值刷新到主内存

5.  释放互斥锁



所以 synchronized 也能保证内存可见性.



**​3. 可重入。**synchronized 同步块对同⼀条线程来说是可重入的,不会出现自己把自己锁死的问题。



public class ThreadSynchronized4 {

public static void main(String[] args) {

    synchronized (ThreadSynchronized4.class) {

        System.out.println("主线程得到锁");

        synchronized (ThreadSynchronized4.class) {

            System.out.println("主线程再次得到锁");

        }

    }

}

}




 ![](https://img-blog.csdnimg.cn/89d89e0c33c743a0adb5071001bcd959.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5r6E55m95piT,size_20,color_FFFFFF,t_70,g_se,x_16)



**注意 :**



1.   加同一把锁。  

     ```

    public class ThreadSynchronized6 {

        private static final int count = 100000;

        static int num = 0;

    

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

            Object obj = new Object();

            Object obj2 = new Object();

            Thread t1 = new Thread(() -> {

                synchronized (obj) {

                    for (int i = 0; i < count; i++) {

                        num++;

                    }

                }

            }, "线程1");

            t1.start();

            Thread t2 = new Thread(() -> {

                synchronized (obj2) {

                    for (int i = 0; i < count; i++) {

                        num--;

                    }



本人从事网路安全工作12年,曾在2个大厂工作过,安全服务、售后服务、售前、攻防比赛、安全讲师、销售经理等职位都做过,对这个行业了解比较全面。


最近遍览了各种网络安全类的文章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。


最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。


![](https://img-blog.csdnimg.cn/img_convert/311903982dea1d8a5d2c98fc271b5b41.jpeg)



### 学习路线图


 其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。


相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。


![](https://img-blog.csdnimg.cn/img_convert/1ddfaf7dc5879b1120e31fafa1ad4dc7.jpeg)


#### 网络安全工具箱


当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份**我自己整理的网络安全入门工具以及使用教程和实战。**


![](https://img-blog.csdnimg.cn/img_convert/bcd1787ce996787388468bb227d8f959.jpeg)


#### 项目实战


最后就是项目实战,这里带来的是**SRC资料&HW资料**,毕竟实战是检验真理的唯一标准嘛~


![](https://img-blog.csdnimg.cn/img_convert/35fc46df24091ce3c9a5032a9919b755.jpeg)


#### 面试题


归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/topics/618540462)**

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值