一、网安学习成长路线图
网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、网安视频合集
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
三、精品网安学习书籍
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、网络安全源码合集+工具包
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、网络安全面试题
最后就是大家最关心的网络安全面试题板块
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
2.3多线程环境使用哈希表
2.3.1Hashtable
2.3.2ConcurrentHashMap
1.JUC常见类
============
JUC的全称:java.util.concurrent(concurrent指的是多线程相关操作)
1.1Callable接口
①Callable是什么:
Callable 是一个 interface . 相当于把线程封装了一个 “返回值”. 方便程序猿借助多线程的方式计算结果.
②为什么Callable接口更适合写这种关于计算的代码?
我们就是为了解决Runnable不方便返回结果这个问题
③我们使用Callable接口来解决这个问题的代码:
a.创建一个匿名内部类 , 实现 Callable 接口, 泛型参数表示返回值的类型。
b.重写 Callable 的 call 方法 , 实现1+2+3+…+1000的执行过程。
c.把 callable 实例使用 FutureTask 包装一下。
b.创建线程 , 线程的构造方法传入 FutureTask , 此时新线程就会执行 FutureTask 内部的 Callable 的。
e.call 方法 , 完成计算, 计算结果就放到了 FutureTask 的 对象task中。
f.在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕 。 并获取到 FutureTask 中的结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class demo2{
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer>callable=new Callable() {
@Override
public Object call() throws Exception {
int sum=0;
for(int i=0;i<=1000;i++){
sum+=i;
}
return sum;
}
};
//为了让线程执行Callable中的任务,光使用构造方法是不够的,还需要使用一个辅助类
FutureTask<Integer>task=new FutureTask<>(callable);
//创建线程,来完成这里的工作
Thread t=new Thread(task);
t.start();
int result = task.get();
System.out.println(result);
}
}
④进一步理解Callable接口:
Callable 和 Runnable 相对, 都是描述一个 “任务”. Callable 描述的是带有返回值的任务,
Runnable 描述的是不带返回值的任务。Callable 通常需要搭配 FutureTask 来使用。 FutureTask 用来保存 Callable 的返回结果.。因为Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定。FutureTask 就可以负责这个等待结果出来的工作。
举个生活中的例子来进一步说明:
当我们去一个餐馆进行吃饭,点单的时候会给我们一个小票,当我们刚拿到小票时显然商家正准备进行加工,而当加工完成将要反馈给我们的时候,他会进行叫号,以便确保是谁的单。而这种反馈叫号的操作很明显就是用上面我们提到的FutureTask等待接收结果的这个行为。
1.2ReentrantLock
①ReentrantLock是什么?
ReentrantLock也是一种可重入锁,和 synchronized很像,两者都是用来实现互斥效果, 保证线程安全的。
②基本用法:(它是把加锁解锁两个操作进行分开的操作)
lock():加锁,如果获取不到就一直死等到获取到为止的操作
trylock(超时时间):加锁,如果一段时间仍然获取不到锁,就放弃加锁
unlock():解锁
③ReentrantLock和Synchronized的区别:
a.ReentrantLock是在JVM外部实现的一个标准库的类(基于Java来实现的),而synchronized是在JVM内部实现的一个关键字(基于C++来实现的)
b.ReentrantLock是需要我们进行手动加锁解锁的,使用起来确实更加灵活,但是很多时候手动释放也会被我们忽视。synchronized不需要手动释放锁,出了相应的代码块后,锁即自动释放。
c.ReentrantLock在锁竞争失败的时候除了阻塞等待以外,可以尝试trylock()来获取到锁,如果失败了就直接返回,给我们留下了更多的余地。而synchronized如果竞争的时候失败就会阻塞等待。
d.ReentrantLock既可以是公平锁,也可以是非公平锁,我们只需要在它的参数位置进行指定(默认是非公平锁,true即是公平锁),而synchronized只是一个非公平锁。
e.ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程。 synchronized 是通过 Object 的 wait / notify 实现等待-唤醒.。每次唤醒的是一 个随机等待的线程,相对而言功能是有限的。
但是在我们的日常工作开发中,synchronized 就够用啦
1.3信号量 Semaphore
①什么是信号量:
信号量, 用来表示 “可用资源的个数”。实质上是一个更广义的锁。(锁也被称为二元信号量)
②举一个通俗的例子来帮助你理解信号量:
自驾去某个地方,我们经常会遇到停车的问题, 可以把信号量想象成是停车场的展示牌: 当前有车位 20 个,表示有 20 个可用资源。 当有车开进去的时候, 就相当于减少(申请资源)了一个可用资源, 可用车位就 -1 (这个称为信号量的 P 操作) 当有车开出来的时候, 就相当于增加(释放资源)一个可用资源, 可用车位就 +1 (这个称为信号量的 V 操作)。 如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源。
Semaphore 的 PV 操作中的加减计数器操作都是原子的, 可以在多线程环境下直接使用。
import java.util.concurrent.Semaphore;
public class demo2{
public static void main(String[] args) throws InterruptedException {
//表示提供了10个资源
Semaphore s=new Semaphore(3);
//资源申请
s.acquire();
System.out.println("申请资源啦");
s.acquire();
System.out.println("申请资源啦");
s.acquire();
System.out.println("申请资源啦");
s.acquire();
System.out.println("申请资源啦");
//释放资源
// s.release(1);
}
}
如图所示,这个时候只有3个资源位,要是我申请了3个后没有释放继续申请,那么程序就会出现阻塞的情况,结果如下图:(也就说只会打印3次)
1.4CountDownLatch
①什么是CountDownLatch?
这用文字不怎么好理解,所以给大家举一个例子:
大家应该都玩过王者荣耀吧,我们都知道最终的胜利是退掉敌方水晶,所以当我们退掉一座塔是不够的,我们需要把最终的水晶退掉,才能够结束这一场对局。
②相关方法的说明:
countDown
给每个线程里面去调用,就表示到达终点了。(就相当于上面提到游戏中每推掉一座塔)
await
是给等待线程去调用.当所有的任务都到达终点了,await
就从阻塞中返回,就表示任务完成。(就相当于推掉水晶)
③代码演示:(注意,要等所有调用完了,即水晶推完,await才会返回。也就才会打印gameover…那句话)
a.没有调用完
b.调用完:
import java.util.concurrent.CountDownLatch;
public class demo2{
public static void main(String[] args) throws InterruptedException {
//在游戏里,包括水晶,我们需要推掉4个塔
CountDownLatch c=new CountDownLatch(4);
for(int i=0;i<4;i++){
Thread t=new Thread(()->{
try {
Thread.sleep(3000);
c.countDown();
System.out.println(Thread.currentThread().getName()+"推掉了1座塔");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
// t.join();不加这个的时候,线程的调度是将是随机的
}
c.await();
System.out.println("gameover!恭喜你获得胜利");
}
写在最后
在结束之际,我想重申的是,学习并非如攀登险峻高峰,而是如滴水穿石般的持久累积。尤其当我们步入工作岗位之后,持之以恒的学习变得愈发不易,如同在茫茫大海中独自划舟,稍有松懈便可能被巨浪吞噬。然而,对于我们程序员而言,学习是生存之本,是我们在激烈市场竞争中立于不败之地的关键。一旦停止学习,我们便如同逆水行舟,不进则退,终将被时代的洪流所淘汰。因此,不断汲取新知识,不仅是对自己的提升,更是对自己的一份珍贵投资。让我们不断磨砺自己,与时代共同进步,书写属于我们的辉煌篇章。
需要完整版PDF学习资源私我
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!