什么时候线程不安全?怎样做到线程安全?怎么扩展线程安全的类?

当多个线程去访问某个类时,如果类会表现出我们预期出现的行为,那么可以称这个类是线程安全的。

什么时候会出现线程不安全?

  • 操作并非原子。多个线程执行某段代码,如果这段代码产生的结果受不同线程之间的执行时序影响,而产生非预期的结果,即发生了竞态条件,就会出现线程不安全;> 常见场景:> > 1.count++。它本身包含三个操作,读取、修改、写入,多线程时,由于线程执行的时序不同,有可能导致两个线程执行后count只加了1,而原有的目标确实希望每次执行都加1;> 2.单例。多个线程可能同时执行到instance == null成立,然后新建了两个对象,而原有目标是希望这个对象永远只有一个;> > > public MyObj getInstance(){>if (instance == null){>instance = new MyObj();>}>return instance> } > > > 解决方式是:当前线程在操作这段代码时,其它线程不能对进行操作> 常见方案:> > 1.单个状态使用 java.util.concurrent.atomic包中的一些原子变量类,注意如果是多个状态就算每个操作是原子的,复合使用的时候并不是原子的;> 2.加锁。比如使用 synchronized 包围对应代码块,保证多线程之间是互斥的,注意应尽可能的只包含在需要作为原子处理的代码块上;> > synchronized的可重入性> =================> > 当线程要去获取它自己已经持有的锁是会成功的,这样的锁是可重入的,synchronized是可重入的> > > class Paxi {>public synchronizedvoid sayHello(){>System.out.println("hello");>}> }> > classMyClass extends Paxi{>public synchronized voiddosomething(){>System.out.println("do thing ..");>super.sayHello();>System.out.println("over");>}> } > > > 它的输出为> > > do thing ..> hello> over > * 修改不可见。读线程无法感知到其它线程写入的值常见场景:> 1.重排序。在没有同步的情况下,编译器、处理器以及运行时等都有可能对操作的执行顺序进行调整,即写的代码顺序和真正的执行顺序不一样,导致读到的是一个失效的值> 2.读取long、double等类型的变量。JVM允许将一个64位的操作分解成两个32位的操作,读写在不同的线程中时,可能读到错误的高低位组合常见方案:> 1.加锁。所有线程都能看到共享变量的最新值;> 2.使用Volatile关键字声明变量。只要对这个变量产生了写操作,那么所有的读操作都会看到这个修改;> 注意:Volatile并不能保证操作的原子性,比如count++操作同样有风险,它仅保证读取时返回最新的值。使用的好处在于访问Volatile变量并不会执行加锁操作,也就不会阻塞线程。不同步的情况下如何做到线程安全?
    ================

1.线程封闭。即仅在单线程内访问数据,线程封闭技术有以下几种:* Ad-hoc线程封闭。即靠自己写程序来实现,比如保证程序只在单线程上对volatile进行 读取-修改-写入* 栈封闭。所有的操作都反生执行线程的栈中,比如在方法中的一个局部变量* ThreadLocal类。内部维护了每个线程和变量的一个独立副本
2.只读共享。即使用不可变的对象。* 使用final去修饰字段,这样这个字段的“值”是不可改变的> 注意final如果修饰的是一个对象引用,比如set,它本身包含的值是可变的* 创建一个不可变的类,来包含多个可变的数据。class OneValue{ //创建不可变对象,创建之后无法修改,事实上这里也没有提供修改的方法private final BigIntegerlast;private final BigInteger[] lastfactor;public OneValue(BigIntegeri,BigInteger[] lastfactor){ this.last=i; this.lastfactor=Arrays.copy(lastfactor,lastfactor.length);} public BigInteger[] getF(BigIntegeri){if(last==null || !last.equals(i)){return null;}else{return Arrays.copy(lastfactor,lastfactor.length)} }}class MyService { //volatile使得cache一经更改,就能被所有线程感知到 private volatile OneValue cache=new OneValue(null,null); public void handle(BigInteger i){ BigInteger[] lastfactor=cache.getF(i); if(lastfactor==null){lastfactor=factor(i);//每次都封装最新的值cache=new OneValue(i,lastfactor) } nextHandle(lastfactor) }} 如何构造线程安全的类?

1.实例封闭。将一个对象封装到另一个对象中,这样能够访问被封装对象的所有代码路径都是已知的,通过合适的加锁策略可以确保被封装对象的访问是线程安全的。> java中的Collections.synchronizedList使用的原理就是这样。部分代码为> > >public static <T> List<T> synchronizedList(List<T> list) {>return (list instanceof RandomAccess ?>new SynchronizedRandomAccessList<>(list) :>new SynchronizedList<>(list));>} > SynchronizedList的实现,注意此处用到的mutex是内置锁> >static class SynchronizedList<E>>extends SynchronizedCollection<E>>implements List<E> {>private static final long serialVersionUID = -7754090372962971524L;>>final List<E> list;> public E get(int index) {>synchronized (mutex) {return list.get(index);}>}>public E set(int index, E element) {>synchronized (mutex) {return list.set(index, element);}>}>public void add(int index, E element) {>synchronized (mutex) {list.add(index, element);}>}>public E remove(int index) {>synchronized (mutex) {return list.remove(index);}>}>} > mutex的实现static class SynchronizedCollection<E> implements Collection<E>, >Serializable {private static final long serialVersionUID = 3053995032091335093L;final Collection<E> c;// Backing Collectionfinal Object mutex; // Object on which to synchronizeSynchronizedCollection(Collection<E> c) {if (c==null)throw new NullPointerException();this.c = c;mutex = this; // mutex实际上就是对象本身} 什么是监视器模式--------java的监视器模式,将对象所有可变状态都封装起来,并由对象自己的内置锁来保护,即是一种实例封闭。比如HashTable就是运用的监视器模式。它的get操作就是用的synchronized,内置锁,来实现的线程安全public synchronized V get(Object key) {Entry tab[] = table;int hash = hash(key);int index = (hash & 0x7FFFFFFF) % tab.length;for (Entry<K,V> e = tab[index] ; e <img src="https://link.juejin.cn/?target=https%3A%2F%2Fdocs.oracle.com%2Fjavase%2Ftutorial%2Fessential%2Fconcurrency%2Flocksync.html "https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html")* 私有锁public class PrivateLock{private Object mylock = new Object(); //私有锁void myMethod(){synchronized(mylock){//do something}}} ```它也可以用来保护对象,相对内置锁,优势在于私有锁可以有多个,同时可以让客户端代码显示的获取私有锁* 类锁在staic方法上修饰的,一个类的所有对象共用一把锁2.把线程安全性委托给线程安全的类如果一个类中的各个组件都是线程安全的,该类是否要处理线程安全问题" style=“margin: auto” />

视情况而定。

1.只有单个组件,且它是线程安全的。public class DVT{private final ConcurrentMap<String,Point> locations;private final Map<String,Point> unmodifiableMap;public DVT(Map<String,Point> points){locations=new ConcurrentHashMap<String,Point>(points);unmodifiableMap=Collections.unmodifiableMap(locations);}public Map<String,Point> getLocations(){return unmodifiableMap;}public Point getLocation(String id){return locations.get(id);}public void setLocation(String id,int x,int y){if(locations.replace(id,new Point(x,y))==null){throw new IllegalArgumentException("invalid "+id);}}}public class Point{public final int x,y;public Point(int x,int y){this.x=x;this.y=y;}} 线程安全性分析* Point类本身是无法更改的,所以它是线程安全的,DVT返回的Point方法也是线程安全的* DVT的方法getLocations返回的对象是不可修改的,是线程安全的* setLocation实际操作的是ConcurrentHashMap它也是线程安全的综上,DVT的安全交给了‘locations’,它本身是线程安全的,DVT本身虽没有任何显示的同步,也是线程安全。这种情况下,就是DVT的线程安全实际是委托给了‘locations’,整个DVT表现出了线程安全。2.线程安全性委托给了多个状态变量只要多个状态变量之间彼此独立,组合的类并不会在其包含的多个状态变量上增加不变性。`依赖的增加则无法保证线程安全````public class NumberRange{private final AtomicInteger lower = new AtomicInteger(0);private final AtomicInteger upper = new AtomicInteger(0);public void setLower(int i){//先检查后执行,存在隐患if (i>upper.get(i)){throw new IllegalArgumentException(‘can not …’);}lower.set(i);}public void setUpper(int i){//先检查后执行,存在隐患if(i<lower.get(i)){throw new IllegalArgumentException(‘can not …’);}upper.set(i);}} ```setLower和setUpper都是‘先检查后执行’的操作,但是没有足够的加锁机制保证操作的原子性。假设原始范围是(0,10),一个线程调用 setLower(5),一个设置setUpper(4)错误的执行时序将可能导致结果为(5,4)如何对现有的线程安全类进行扩展?

假设需要扩展的功能为 ‘没有就添加’。

1.直接修改原有的代码。但通常没有办法修改源代码
2.继承。继承原有的代码,添加新的功能。但是同步策略保存在两份文件中,如果底层同步策略变更,很容易出问题
两份文件中,如果底层同步策略变更,很容易出问题
3.组合。将类放入一个辅助类中,通过辅助类的操作代码。 比如扩展 Collections.synchronizedList。期间需要注意锁的机制,错误方式为public class ListHelper<E>{public List<E> list=Collections.synchronizedList(new ArrayList<E>());...public synchronized boolean putIfAbsent(E x){boolean absent = !list.contains(x);if(absent){ list.add(x);}return absent;}} 这里的putIfAbsent并不能带来线程安全,原因是list的内置锁并不是ListHelper,也就是putIfAbsent相对list的其它方法并不是原子的。Collections.synchronizedList是锁在list本身的,正确方式为publicboolean putIfAbsent(E x){synchronized(list){boolean absent = !list.contains(x);if(absent){list.add(x);}return absent;}} > 另外可以不管要操作的类是否是线程安全,对类统一添加一层额外的锁。 实现参考Collections.synchronizedList方法学习网络安全技术的方法无非三种:

第一种是报网络安全专业,现在叫网络空间安全专业,主要专业课程:程序设计、计算机组成原理原理、数据结构、操作系统原理、数据库系统、 计算机网络、人工智能、自然语言处理、社会计算、网络安全法律法规、网络安全、内容安全、数字取证、机器学习,多媒体技术,信息检索、舆情分析等。

第二种是自学,就是在网上找资源、找教程,或者是想办法认识一-些大佬,抱紧大腿,不过这种方法很耗时间,而且学习没有规划,可能很长一段时间感觉自己没有进步,容易劝退。

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

第三种就是去找培训。

image.png

接下来,我会教你零基础入门快速入门上手网络安全。

网络安全入门到底是先学编程还是先学计算机基础?这是一个争议比较大的问题,有的人会建议先学编程,而有的人会建议先学计算机基础,其实这都是要学的。而且这些对学习网络安全来说非常重要。但是对于完全零基础的人来说又或者急于转行的人来说,学习编程或者计算机基础对他们来说都有一定的难度,并且花费时间太长。

第一阶段:基础准备 4周~6周

这个阶段是所有准备进入安全行业必学的部分,俗话说:基础不劳,地动山摇
image.png

第二阶段:web渗透

学习基础 时间:1周 ~ 2周:

① 了解基本概念:(SQL注入、XSS、上传、CSRF、一句话木马、等)为之后的WEB渗透测试打下基础。
② 查看一些论坛的一些Web渗透,学一学案例的思路,每一个站点都不一样,所以思路是主要的。
③ 学会提问的艺术,如果遇到不懂得要善于提问。
image.png

配置渗透环境 时间:3周 ~ 4周:

① 了解渗透测试常用的工具,例如(AWVS、SQLMAP、NMAP、BURP、中国菜刀等)。
② 下载这些工具无后门版本并且安装到计算机上。
③ 了解这些工具的使用场景,懂得基本的使用,推荐在Google上查找。

渗透实战操作 时间:约6周:

① 在网上搜索渗透实战案例,深入了解SQL注入、文件上传、解析漏洞等在实战中的使用。
② 自己搭建漏洞环境测试,推荐DWVA,SQLi-labs,Upload-labs,bWAPP。
③ 懂得渗透测试的阶段,每一个阶段需要做那些动作:例如PTES渗透测试执行标准。
④ 深入研究手工SQL注入,寻找绕过waf的方法,制作自己的脚本。
⑤ 研究文件上传的原理,如何进行截断、双重后缀欺骗(IIS、PHP)、解析漏洞利用(IIS、Nignix、Apache)等,参照:上传攻击框架。
⑥ 了解XSS形成原理和种类,在DWVA中进行实践,使用一个含有XSS漏洞的cms,安装安全狗等进行测试。
⑦ 了解一句话木马,并尝试编写过狗一句话。
⑧ 研究在Windows和Linux下的提升权限,Google关键词:提权
image.png
以上就是入门阶段

第三阶段:进阶

已经入门并且找到工作之后又该怎么进阶?详情看下图
image.png

给新手小白的入门建议:
新手入门学习最好还是从视频入手进行学习,视频的浅显易懂相比起晦涩的文字而言更容易吸收,这里我给大家准备了一套网络安全从入门到精通的视频学习资料包免费领取哦!

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值