java线程安全总结

一、网安学习成长路线图

网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

二、网安视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
在这里插入图片描述

三、精品网安学习书籍

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
在这里插入图片描述

四、网络安全源码合集+工具包

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)

在这里插入图片描述

五、网络安全面试题

最后就是大家最关心的网络安全面试题板块
在这里插入图片描述在这里插入图片描述

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

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

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

balance = balance - num;

}

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

Account account = new Account(1000);

Thread a = new Thread(new AddThread(account, 20), “add”);

Thread b = new Thread(new WithdrawThread(account, 20), “withdraw”);

a.start();

b.start();

a.join();

b.join();

System.out.println(account.getBalance());

}

static class AddThread implements Runnable {

Account account;

int     amount;

public AddThread(Account account, int amount) {

this.account = account;

this.amount = amount;

}

public void run() {

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

account.add(amount);

}

}

}

static class WithdrawThread implements Runnable {

Account account;

int     amount;

public WithdrawThread(Account account, int amount) {

this.account = account;

this.amount = amount;

}

public void run() {

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

account.withdraw(amount);

}

}

}

}

第一次执行结果为10200,第二次执行结果为1060,每次执行的结果都是不确定的,因为线程的执行顺序是不可预见的。这是java同步产生的根源,synchronized关键字保证了多个线程对于同步块是互斥的,synchronized作为一种同步手段,解决java多线程的执行有序性和内存可见性,而volatile关键字之解决多线程的内存可见性问题。后面将会详细介绍。

synchronized关键字

上面说了,java用synchronized关键字做为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,这一段代码成为互斥区或临界区,为了保证共享变量的正确性,synchronized标示了临界区。典型的用法如下:

Java代码  

  1. synchronized(锁){

  2. 临界区代码

  3. }

synchronized(锁){

临界区代码

}

为了保证银行账户的安全,可以操作账户的方法如下:

Java代码  

  1. public synchronized void add(int num) {

  2. balance = balance + num;

  3. }

  4. public synchronized void withdraw(int num) {

  5. balance = balance - num;

  6. }

public synchronized void add(int num) {

balance = balance + num;

}

public synchronized void withdraw(int num) {

balance = balance - num;

}

刚才不是说了synchronized的用法是这样的吗:

Java代码  

  1. synchronized(锁){

  2. 临界区代码

  3. }

synchronized(锁){

临界区代码

}

那么对于public synchronized void add(int num)这种情况,意味着什么呢?其实这种情况,锁就是这个方法所在的对象。同理,如果方法是public  static synchronized void add(int num),那么锁就是这个方法所在的class。

理论上,每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁是没有意义的。假如有这样的代码:

Java代码  

  1. public class ThreadTest{

  2. public void test(){

  3. Object lock=new Object();

  4. synchronized (lock){

  5. //do something

  6. }

  7. }

  8. }

public class ThreadTest{

public void test(){

Object lock=new Object();

synchronized (lock){

//do something

}

}

}

lock变量作为一个锁存在根本没有意义,因为它根本不是共享对象,每个线程进来都会执行Object lock=new Object();每个线程都有自己的lock,根本不存在锁竞争。

每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待cpu的调度。当一开始线程a第一次执行account.add方法时,jvm会检查锁对象account 的就绪队列是否已经有线程在等待,如果有则表明account的锁已经被占用了,由于是第一次运行,account的就绪队列为空,所以线程a获得了锁,执行account.add方法。如果恰好在这个时候,线程b要执行account.withdraw方法,因为线程a已经获得了锁还没有释放,所以线程 b要进入account的就绪队列,等到得到锁后才可以执行。

一个线程执行临界区代码过程如下:

1 获得同步锁

2 清空工作内存

3 从主存拷贝变量副本到工作内存

4 对这些变量计算

5 将变量从工作内存写回到主存

6 释放锁

可见,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性。

生产者/消费者模式

生产者/消费者模式其实是一种很经典的线程同步模型,很多时候,并不是光保证多个线程对某共享资源操作的互斥性就够了,往往多个线程之间都是有协作的。

假设有这样一种情况,有一个桌子,桌子上面有一个盘子,盘子里只能放一颗鸡蛋,A专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B专门从盘子里拿鸡蛋,如果盘子里没鸡蛋,则等待直到盘子里有鸡蛋。其实盘子就是一个互斥区,每次往盘子放鸡蛋应该都是互斥的,A的等待其实就是主动放弃锁,B 等待时还要提醒A放鸡蛋。

如何让线程主动释放锁

很简单,调用锁的wait()方法就好。wait方法是从Object来的,所以任意对象都有这个方法。看这个代码片段:

Java代码  

  1. Object lock=new Object();//声明了一个对象作为锁

  2. synchronized (lock) {

  3. balance = balance - num;

  4. //这里放弃了同步锁,好不容易得到,又放弃了

  5. lock.wait();

  6. }

Object lock=new Object();//声明了一个对象作为锁

synchronized (lock) {

balance = balance - num;

//这里放弃了同步锁,好不容易得到,又放弃了

lock.wait();

}

如果一个线程获得了锁lock,进入了同步块,执行lock.wait(),那么这个线程会进入到lock的阻塞队列。如果调用 lock.notify()则会通知阻塞队列的某个线程进入就绪队列。

声明一个盘子,只能放一个鸡蛋

Java代码  

  1. import java.util.ArrayList;

  2. import java.util.List;

  3. public class Plate {

  4. List eggs = new ArrayList();

  5. public synchronized Object getEgg() {

  6. while(eggs.size() == 0) {

  7. try {

  8. wait();

  9. } catch (InterruptedException e) {

  10. }

  11. }

  12. Object egg = eggs.get(0);

  13. eggs.clear();// 清空盘子

  14. notify();// 唤醒阻塞队列的某线程到就绪队列

  15. System.out.println(“拿到鸡蛋”);

  16. return egg;

  17. }

  18. public synchronized void putEgg(Object egg) {

  19. while(eggs.size() > 0) {

  20. try {

  21. wait();

  22. } catch (InterruptedException e) {

  23. }

  24. }

  25. eggs.add(egg);// 往盘子里放鸡蛋

  26. notify();// 唤醒阻塞队列的某线程到就绪队列

  27. System.out.println(“放入鸡蛋”);

  28. }

  29. static class AddThread extends Thread{

  30. private Plate plate;

  31. private Object egg=new Object();

  32. public AddThread(Plate plate){

  33. this.plate=plate;

  34. }

  35. public void run(){

  36. for(int i=0;i<5;i++){

  37. plate.putEgg(egg);

  38. }

  39. }

  40. }

  41. static class GetThread extends Thread{

  42. private Plate plate;

  43. public GetThread(Plate plate){

  44. this.plate=plate;

  45. }

  46. public void run(){

  47. for(int i=0;i<5;i++){

  48. plate.getEgg();

  49. }

  50. }

  51. }

  52. public static void main(String args[]){

  53. try {

  54. Plate plate=new Plate();

  55. Thread add=new Thread(new AddThread(plate));

  56. Thread get=new Thread(new GetThread(plate));

  57. add.start();

  58. get.start();

  59. add.join();

  60. get.join();

  61. } catch (InterruptedException e) {

  62. e.printStackTrace();

  63. }

  64. System.out.println(“测试结束”);

  65. }

  66. }

import java.util.ArrayList;

import java.util.List;

public class Plate {

List eggs = new ArrayList();

public synchronized Object getEgg() {

while(eggs.size() == 0) {

try {

wait();

} catch (InterruptedException e) {

}

}

Object egg = eggs.get(0);

eggs.clear();// 清空盘子

notify();// 唤醒阻塞队列的某线程到就绪队列

System.out.println(“拿到鸡蛋”);

return egg;

}

public synchronized void putEgg(Object egg) {

while(eggs.size() > 0) {

try {

wait();

} catch (InterruptedException e) {

}

}

eggs.add(egg);// 往盘子里放鸡蛋

notify();// 唤醒阻塞队列的某线程到就绪队列

System.out.println(“放入鸡蛋”);

}

static class AddThread extends Thread{

private Plate plate;

private Object egg=new Object();

public AddThread(Plate plate){

this.plate=plate;

}

public void run(){

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

plate.putEgg(egg);

}

}

}

static class GetThread extends Thread{

private Plate plate;

public GetThread(Plate plate){

this.plate=plate;

}

public void run(){

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

plate.getEgg();

}

}

}

public static void main(String args[]){

try {

Plate plate=new Plate();

Thread add=new Thread(new AddThread(plate));

Thread get=new Thread(new GetThread(plate));

add.start();

get.start();

add.join();

get.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“测试结束”);

}

}

执行结果:

Html代码  

  1. 放入鸡蛋

  2. 拿到鸡蛋

  3. 放入鸡蛋

  4. 拿到鸡蛋

  5. 放入鸡蛋

  6. 拿到鸡蛋

  7. 放入鸡蛋

  8. 拿到鸡蛋

  9. 放入鸡蛋

  10. 拿到鸡蛋

  11. 测试结束

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

测试结束

声明一个Plate对象为plate,被线程A和线程B共享,A专门放鸡蛋,B专门拿鸡蛋。假设

1 开始,A调用plate.putEgg方法,此时eggs.size()为0,因此顺利将鸡蛋放到盘子,还执行了notify()方法,唤醒锁的阻塞队列的线程,此时阻塞队列还没有线程。

2 又有一个A线程对象调用plate.putEgg方法,此时eggs.size()不为0,调用wait()方法,自己进入了锁对象的阻塞队列。

3 此时,来了一个B线程对象,调用plate.getEgg方法,eggs.size()不为0,顺利的拿到了一个鸡蛋,还执行了notify()方法,唤醒锁的阻塞队列的线程,此时阻塞队列有一个A线程对象,唤醒后,它进入到就绪队列,就绪队列也就它一个,因此马上得到锁,开始往盘子里放鸡蛋,此时盘子是空的,因此放鸡蛋成功。

4 假设接着来了线程A,就重复2;假设来料线程B,就重复3。

整个过程都保证了放鸡蛋,拿鸡蛋,放鸡蛋,拿鸡蛋。

volatile关键字

volatile是java提供的一种同步手段,只不过它是轻量级的同步,为什么这么说,因为volatile只能保证多线程的内存可见性,不能保证多线程的执行有序性。而最彻底的同步要保证有序性和可见性,例如synchronized。任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存。因此对于Valatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的。什么意思呢?假如有这样的代码:

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

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

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

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

93道网络安全面试题

需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)

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

黑客学习资源推荐

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

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

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

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

image

② 路线对应学习视频

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

image-20231025112050764

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值