并发最佳实践

本文是我们名为“ 高级Java ”的学院课程的一部分。

本课程旨在帮助您最有效地使用Java。 它讨论了高级主题,包括对象创建,并发,序列化,反射等。 它将指导您完成Java掌握的过程! 在这里查看 !

目录

1.简介 2.线程和线程组 3.并发,同步和不变性 4.期货,执行人和线程池 5.锁 6.线程调度器 7.原子操作 8.并行收集 9.探索Java标准库 10.明智地使用同步 11.等待/通知 12.对并发问题进行故障排除 13.接下来是什么 14.下载

1.简介

多处理器和多核硬件体系结构极大地影响了当今运行在其上的应用程序的设计和执行模型。 为了充分利用可用计算单元的全部功能,应用程序应准备好支持同时运行并争夺资源和内存的多个执行流。 并发编程带来了许多与数据访问和事件的不确定性相关的挑战,这些挑战可能导致意外崩溃和奇怪的故障。

在本教程的这一部分中,我们将研究Java可以为开发人员提供什么,以帮助他们在并发世界中编写健壮而安全的应用程序。

2.线程和线程组

线程是Java中并发应用程序的基础构建块。 线程有时被称为轻量级进程 ,它们允许多个执行流并发进行。 Java中的每个应用程序都有至少一个称为主线程的线程 。 每个Java线程仅存在于JVM内部,并且可能不反映任何操作系统线程。

Java中的ThreadThread类的实例。 通常,不建议使用Thread类的实例直接创建和管理线程( Futures和Executors部分中介绍的执行器和线程池提供了一种更好的方法),但是这样做非常容易:

public static void main(String[] args) {new Thread( new Runnable() {@Overridepublic void run() {// Some implementation here}} ).start();
}

或使用Java 8 lambda函数的相同示例:

public static void main(String[] args) {new Thread( () -> { /* Some implementation here */ } ).start();
}

但是,用Java创建新线程看起来非常简单,线程具有复杂的生命周期,并且可以处于以下状态之一(在给定的时间点,线程只能处于一种状态)。

线程状态 描述
NEW 尚未启动的线程处于此状态。
RUNNABLE 在Java虚拟机中执行的线程处于这种状态。
BLOCKED 等待监视器锁定而被阻塞的线程处于此状态。
WAITING 无限期地等待另一个线程执行特定操作的线程处于此状态。
TIMED_WAITING 无限期地等待另一个线程执行特定操作的线程处于此状态。
TERMINATED 退出的线程处于此状态。

表格1

目前并不是所有的线程状态都明确,但是在本教程的后面,我们将介绍其中的大多数内容,并讨论导致线程处于一种或另一种状态的事件类型。

线程可以组装成组。 线程组代表一组线程,并且还可以包括其他线程组(因此形成树)。 线程组旨在成为一个不错的功能,但是由于执行程序和线程池(请参阅Futures,Executor和Thread Pools )是更好的替代方法,因此如今不建议使用它们。

3.并发,同步和不变性

在大多数每个Java应用程序中,都需要多个运行线程相互通信并访问共享数据。 读取这些数据并不是什么大问题,但是对其进行不协调的修改将直接导致灾难(所谓的赛车状况)。 这就是英寸同步 同步踢是确保在同一时间几个同时运行的线程将不执行的应用程序代码的具体守卫(同步)块中的机构的点。 如果其中一个线程已开始执行代码的同步块,则任何其他试图执行同一块的线程都必须等待,直到第一个线程完成。

Java语言具有内置的同步支持,形式为synchronized关键字。 该关键字可以应用于实例方法,静态方法,也可以在任意执行块周围使用,并确保一次仅一个线程将能够调用它。 例如:

public synchronized void performAction() {// Some implementation here
}public static synchronized void performClassAction() {// Some implementation here
}

或者,使用与代码块同步的示例:

public void performActionBlock() {synchronized( this ) {// Some implementation here}
}

synchronized关键字还有另一个非常重要的作用:它为同一对象的synchronized方法或代码块的任何调用自动建立先发生的关系( http://en.wikipedia.org/wiki/Happened-before )。 这保证了对象状态的更改对所有线程都是可见的。

请注意,构造函数无法同步(将synchronized关键字与构造函数一起使用会引起编译器错误),因为在构造实例时,只有创建实例的线程才能访问它。

在Java中,同步是围绕称为监视器的内部实体(或固有/监视器锁定, http://en.wikipedia.org/wiki/Monitor_ (synchronization))构建的。 Monitor强制对对象状态进行独占访问,并建立事前关联。 当任何线程调用synchronized方法时,它将自动获取该方法实例(或静态方法中的类)的内在(监视)锁,并在方法返回时释放它。

最后,同步是Java可重入的 :这意味着线程可以获取它已经拥有的锁。 由于线程具有较少的阻塞自身的机会,因此重新进入可大大简化并发应用程序的编程模型。

如您所见,并发在Java应用程序中引入了很多复杂性。 但是,有一种解决方法: 不变性 。 我们已经讨论过很多次了,但是对于多线程应用程序来说,这确实非常重要:不可变对象不需要同步,因为它们永远不会被多个线程更新。

4.期货,执行人和线程池

用Java创建新线程很容易,但是管理它们确实很困难。 Java标准库以执行程序和线程池的形式提供了极其有用的抽象,旨在简化线程管理。

本质上,在其最简单的实现中,线程池创建并维护线程列表,这些线程列表可立即使用。 应用程序无需每次都生成新线程,而只是从池中借用一个(或所需的多个线程)。 一旦借用的线程完成其工作,它将返回到池中,并可以用来接管下一个任务。

尽管可以直接使用线程池,但是Java标准库提供了执行程序外观,该外观具有一组工厂方法来创建常用的线程池配置。 例如,下面的代码段创建了一个具有固定线程数(10)的线程池:

ExecutorService executor = Executors.newFixedThreadPool( 10 );

执行程序可以用来卸载任何任务,因此它将在与线程池分开的线程中执行(注意,不建议将执行程序用于长时间运行的任务)。 执行程序的外观允许自定义基础线程池的行为,并支持以下配置:

方法 描述
Executors.newCachedThreadPool 创建一个线程池,该线程池根据需要创建新线程,但是将在先前构造的线程可用时重用它们。
Executors.newFixedThreadPool 创建一个线程池,该线程池重用在共享的无边界队列上运行的固定数量的线程。
Executors.newScheduledThreadPool 创建一个线程池,该线程池可以安排命令在给定的延迟后运行或定期执行。
Executors.newSingleThreadExecutor 创建一个执行程序,该执行程序使用在不受限制的队列上操作的单个工作线程。
Executors.newSingleThreadScheduledExecutor 创建一个单线程执行器,该执行器可以计划命令在给定的延迟后运行或定期执行。

表2

在某些情况下,执行的结果不是很重要,因此执行程序支持即发即弃的语义,例如:

executor.execute( new Runnable() {            @Overridepublic void run() {// Some implementation here}
} );

等效的Java 8示例更加简洁:

executor.execute( () -> {// Some implementation here
} );

但是,如果执行的结果很重要,则Java标准库会提供另一个抽象来表示将来会发生的计算,称为Future<T> 。 例如:

Future< Long > result = executor.submit( new Callable< Long >() {@Overridepublic Long call() throws Exception {// Some implementation herereturn ...;}            
} );

Future<T>的结果可能无法立即获得,因此应用程序应使用一系列get(…)方法来等待它。 例如:

Long value = result.get( 1, TimeUnit.SECONDS );

如果计算结果在指定的超时时间内不可用,则将引发TimeoutException异常。 有一个重载版本的get()会一直等待,但是请您优先使用超时的版本。

自Java 8发行以来,开发人员拥有Future<T>另一个版本CompletableFuture<T> ,该版本支持在其完成时触发的附加功能和操作。 不仅如此,通过引入流,Java 8引入了一种简单,非常直接的方式来使用parallelStream()方法执行并行收集处理,例如:

final Collection< String > strings = new ArrayList<>();
// Some implementation herefinal int sumOfLengths = strings.parallelStream().filter( str -> !str.isEmpty() ).mapToInt( str -> str.length() ).sum();

执行程序和并行流带到Java平台的简单性使Java中的并行和并行编程变得更加容易。 但是有一个陷阱:线程池和并行流的不受控制的创建可能会破坏应用程序的性能,因此,对它们进行相应的管理很重要。

5.锁

除了监视器之外,Java还支持可重入互斥锁(具有与监视器锁相同的基本行为和语义,但具有更多功能)。 这些锁可通过java.util.concurrent.locks包中的ReentrantLock类获得。 这是一个典型的锁用法习惯用法:

private final ReentrantLock lock = new ReentrantLock();public void performAction() {lock.lock();try { // Some implementation here} finally {lock.unlock();}
}

请注意,必须通过调用unlock()方法显式地释放任何锁(对于synchronized方法和执行块,Java编译器在内部发出释放监视器锁的指令)。 如果锁需要编写更多的代码,为什么它们比监视器更好? 好吧,出于几个原因,但最重要的是,锁可以在等待获取时使用超时并快速失败(监控器始终无限期地等待,并且无法指定所需的超时)。 例如:

public void performActionWithTimeout() throws InterruptedException {if( lock.tryLock( 1, TimeUnit.SECONDS ) ) {try {// Some implementation here} finally {lock.unlock();}}
}

现在,当我们对监视器和锁有了足够的了解时,让我们讨论它们的使用如何影响线程状态。

当任何线程正在使用lock()方法调用等待锁(由另一个线程获取lock() ,它处于WAITING状态。 但是,当任何线程使用带有超时的tryLock()方法调用等待锁(由另一个线程获取tryLock()时,它处于TIMED_WAITING状态。 相反,当任何线程正在使用synchronized方法或执行块等待监视器(由另一个线程获取)时,它处于BLOCKED状态。

到目前为止,我们看到的示例非常简单,但是锁管理确实很困难,而且充满陷阱。 其中最臭名昭著的是僵局:两个或两个以上竞争线程正在互相等待进行操作,因此从来没有这样做的情况。 当涉及多个锁或监视器锁时,通常会发生死锁。 JVM通常能够检测正在运行的应用程序中的死锁并警告开发人员(请参阅“并发问题疑难解答”部分)。 僵局的典型示例如下所示:

private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();public void performAction() {lock1.lock();try {// Some implementation here            try {lock2.lock();                // Some implementation here} finally {lock2.unlock();}              // Some implementation here} finally {lock1.unlock();}
}public void performAnotherAction() {lock2.lock();try {// Some implementation here            try {lock1.lock();                // Some implementation here} finally {lock1.unlock();}              // Some implementation here} finally {lock2.unlock();}
}

performAction()方法尝试先获取lock1 ,然后获取lock2 ,而performAnotherAction()方法lock2不同的顺序lock2它, lock2 ,再获取lock1 。 如果通过程序执行流在两个不同线程中的同一类实例上调用这两个方法,则很可能发生死锁:第一个线程将无限期地等待第二个线程获取的lock2 ,而第二个线程将无限期等待无限期地等待第一个获得的lock1

6.线程调度器

在JVM中,线程调度程序确定应运行哪个线程以及运行多长时间。 Java应用程序创建的所有线程都具有优先级,该优先级在决定何时调度线程及其时间范围时,会基本上影响线程调度算法。 但是,此功能的声誉是不可移植的(因为大多数技巧都依赖于线程调度程序的特定行为)。

Thread类还通过使用yield()方法提供了另一种干预线程调度实现的方法。 它暗示线程调度程序当前线程愿意放弃其当前使用的处理器时间(并且还具有不可移植的声誉)。

总的来说,依靠Java线程调度程序的实现细节并不是一个好主意。 这就是为什么Java标准库中的执行者和线程池(请参阅“ 期货,执行者和线程池”部分)试图不向开发人员公开那些不可移植的细节(但如果确实有必要的话,仍然可以这样做) )。 没有什么比精心设计更好地工作了,精心设计试图考虑应用程序所运行的实际硬件(例如,使用Runtime类可以轻松检索可用的CPU和内核数量)。

7.原子操作

在多线程世界中,有一组特定的指令称为比较交换 (CAS)。 这些指令将它们的值与给定值进行比较,并且只有它们相同时,才设置新的给定值。 这是通过单个原子操作完成的,该操作通常是无锁且高效的。

Java标准库中有大量支持原子操作的类列表,所有这些类都位于java.util.concurrent.atomic包下。

描述
AtomicBoolean 可以自动更新的布尔
AtomicInteger 一个可以自动更新的int值。
AtomicIntegerArray 一个可能会自动更新的值。
AtomicLongArray 一个数组,其中的元素可以原子更新。
AtomicReference<V> 可以原子更新的对象引用。
AtomicReferenceArray<E> 对象引用的数组,其中的元素可以原子更新。

表3

Java 8版本通过一组新的原子操作(累加器和加法器)扩展了java.util.concurrent.atomic

描述
DoubleAccumulator 一个或多个变量一起保持使用提供的函数更新的运行双精度值。
DoubleAdder 一个或多个变量共同保持初始为零的双和。
LongAccumulator 一个或多个变量一起保持使用提供的函数更新的运行时长值。
LongAdder 一个或多个变量共同保持初始为零的长和。

表4

8.并行收集

可以由多个线程访问和修改的共享集合不是一个例外,而是一个规则。 Java标准库在Collections类中提供了两个有用的静态方法,这些方法使任何现有的collection都是线程安全的。 例如:

final Set< String > strings = Collections.synchronizedSet( new HashSet< String >() );final Map< String, String > keys = Collections.synchronizedMap( new HashMap< String, String >() );

但是,返回的通用集合包装器是线程安全的,通常不是最好的选择,因为它们在实际应用程序中的性能相当中等。 这就是为什么Java标准库包含一组针对并发调整的丰富的收集类的原因。 以下只是最广泛使用的列表,所有列表都托管在java.util.concurrent包下。

描述
ArrayBlockingQueue<E> 由数组支持的有界阻塞队列。
ConcurrentHashMap<K,V> 哈希表支持检索的完全并发性和可更新的可调整预期并发性。
ConcurrentLinkedDeque<E> 基于链接节点的无限制并发双端队列。
ConcurrentLinkedQueue<E> 基于链接节点的无界线程安全队列。
ConcurrentSkipListMap<K,V> 可扩展的并发地图实现
ConcurrentSkipListSet<E> 基于ConcurrentSkipListMap可伸缩并发集实现。
CopyOnWriteArrayList<E> ArrayList的线程安全变体,其中所有可变操作(添加,设置等)都通过对基础数组进行全新复制来实现。
CopyOnWriteArraySet<E> 一个使用内部CopyOnWriteArrayList进行所有操作的Set。
DelayQueue<E extends Delayed> 延迟元素的无限制阻塞队列,在该队列中,仅当元素的延迟到期时才可以使用该元素。
LinkedBlockingDeque<E> 基于链接节点的可选绑定的阻塞双端队列。
LinkedBlockingQueue<E> 基于链接节点的可选绑定的阻塞队列。
LinkedTransferQueue<E> 基于链接节点的无界TransferQueue
PriorityBlockingQueue<E> 一个无界阻塞队列,它使用与类PriorityQueue相同的排序规则,并提供阻塞检索操作。
SynchronousQueue<E> 一个阻塞队列,其中每个插入操作必须等待另一个线程进行相应的删除操作,反之亦然。

表5

这些类是专门为在多线程应用程序中使用而设计的。 他们利用许多技术来使对集合的并发访问尽可能高效,并且是synchronized集合包装器的推荐替代者。

9.探索Java标准库

对于编写并发应用程序的Java开发人员来说, java.util.concurrentjava.util.concurrent.locks包是真正的瑰宝。 由于那里有很多类,因此在本节中,我们将介绍其中最有用的类,但是请不要犹豫地查阅Java官方文档并进行探索。

描述
CountDownLatch 一种同步帮助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成为止。
CyclicBarrier 一种同步辅助工具,它允许一组线程全部互相等待以到达一个公共的障碍点。
Exchanger<V> 线程可以配对并在配对中交换元素的同步点。
Phaser 可重用的同步屏障,其功能类似于CyclicBarrierCountDownLatch但支持更灵活的用法。
Semaphore 计数信号量。
ThreadLocalRandom 隔离到当前线程的随机数生成器
ReentrantReadWriteLock 读/写锁的实现

表6

不幸的是, ReentrantReadWriteLock的Java实现不是那么出色,从Java 8开始,有了新的锁:

描述
StampedLock 一种基于功能的锁,具有三种模式来控制读/写访问。

表7

10.明智地使用同步

锁定和synchronized关键字是功能强大的工具,可在多线程应用程序中极大地帮助保持数据模型和程序状态的一致性。 但是,不明智地使用它们会导致线程争用,并且可能会大大降低应用程序性能。 另一方面,不使用同步原语可能(并且将)导致怪异的程序状态和数据损坏,最终导致应用程序崩溃。 因此,平衡很重要。

建议是在确实需要的地方尝试使用锁或/和synchronized 。 在执行此操作时,请确保尽快释放锁定,并且将需要锁定或同步的执行块保持在最小限度。 那些技术至少应该有助于减少竞争,但不会消除竞争。

近年来,出现了许多所谓的无锁算法和数据结构(例如, Atomic Operations部分中的Java 原子操作 )。 与使用同步原语构建的等效实现相比,它们提供了更好的性能。

很高兴知道JVM有一些运行时优化,以消除可能不必要的锁定。 最有名的是偏压锁定 :一种优化,它通过消除与Java同步原语相关的操作来提高无竞争的同步性能(有关更多详细信息,请参阅http://www.oracle.com/technetwork/java/6-performance-137236 .html#2.1.1 )。

不过,JVM会尽力而为,消除应用程序中不必要的同步是更好的选择。 过多使用同步会对应用程序性能产生负面影响,因为线程将浪费昂贵的CPU周期来争夺资源,而不是进行实际工作。

11.等待/通知

在Java标准库( java.util.concurrent )中引入并发实用程序之前,使用Objectwait()/notify()/notifyAll()方法是在Java中的线程之间建立通信的方式。 仅当线程拥有有关对象的监视器时,才必须调用所有这些方法 。 例如:

private Object lock = new Object();public void performAction() {synchronized( lock ) {while( <condition> ) {// Causes the current thread to wait until// another thread invokes the notify() or notifyAll() methods.lock.wait();}// Some implementation here        }
}

方法wait()释放当前线程持有的监视器锁定,因为它尚未满足其等待的条件( wait()方法必须在循环中调用,并且绝不能在循环外部调用 )。 因此,在同一监视器上等待的另一个线程有机会运行。 完成此线程后,它应调用notify()/notifyAll()方法之一来唤醒等待监视器锁定的一个或多个线程。 例如:

public void performAnotherAction() {synchronized( lock ) {              // Some implementation here// Wakes up a single thread that is waiting on this object's monitor.lock.notify();}
}

notify()notifyAll()之间的区别在于,第一个唤醒单个线程,而第二个唤醒所有等待的线程(它们开始争夺监视器锁定)。

不建议在现代Java应用程序中使用wait()/notify()习惯用法。 它不仅复杂,还需要遵循一组强制性规则。 因此,它可能会在正在运行的程序中引起细微的错误,这将是非常困难且耗时的调查。 java.util.concurrent有很多可以用更简单的替代方法来替换wait()/notify()方法(在现实情况下,它很可能会具有更好的性能)。

12.对并发问题进行故障排除

在多线程应用程序中,很多事情可能出错。 复制问题成为噩梦。 调试和故障排除可能要花费数小时甚至数天甚至数周。 Java开发工具包(JDK)包括几个工具,这些工具至少能够提供有关应用程序线程及其状态的一些详细信息,并诊断死锁条件(请参阅线程和线程组以及锁部分)。 首先是一个好点。 这些工具是(但不限于):

  • JVisualVM ( http://docs.oracle.com/javase/7/docs/technotes/tools/share/jvisualvm.html )
  • Java任务控制 ( http://docs.oracle.com/javacomponents/jmc.htm )
  • jstack ( https://docs.oracle.com/javase/7/docs/technotes/tools/share/jstack.html )

13.接下来是什么

在这一部分中,我们研究了现代软件和硬件平台的非常重要的方面-并发性。 特别是,我们已经看到Java作为一种语言及其标准库为开发人员提供了哪些工具,以帮助他们处理并发和异步执行。 在本教程的下一部分中,我们将介绍Java中的序列化技术。

14.下载

您可以在此处下载本课程的源代码: advanced-java-part-9

翻译自: https://www.javacodegeeks.com/2015/09/concurrency-best-practices.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/358131.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Fastjson反序列化漏洞复现(实战案例)

漏洞介绍 FastJson在解析json的过程中&#xff0c;支持使用autoType来实例化某一个具体的类&#xff0c;并调用该类的set/get方法来访问属性。通过查找代码中相关的方法&#xff0c;即可构造出一些恶意利用链。 通俗理解就是&#xff1a;漏洞利用fastjson autotype在处理json对…

SVN Error: Can‘t connect to host xxxxx‘: 由于目标计算机积极拒绝,无法连接,的最快解决办法

孙叫兽&#xff0c;前端全栈&#xff0c;微信公众号&#xff1a;电商程序员。如果本文对你有帮助&#xff0c;记得点赞关注&#xff0c;谢谢&#xff01; 每次换个新电脑&#xff0c;新项目&#xff0c;新场地&#xff0c;新公司都有可能使用SVN下载老项目,有的是用内网&#x…

夺命雷公狗---无限级分类NO3

<?phpheader("Content-Type:text/html;charsetutf-8");/*无限级分类。牵扯2个应用0是-找指定栏目的子栏目1是-找指定栏目的子孙栏目&#xff0c;即子孙树2是-找指定栏目的父栏目/父父栏目....顶级栏目&#xff0c;即家谱树*/$aarr array(array(id>1,name>…

百度地图接口调用

当我们网站需要调用百度地图接口的时候&#xff0c;可以使用如下方法&#xff1a; 1.如何获取经纬度坐标 1.打开百度地图&#xff0c;在百度地图最底部找到“地图开放平台”链接。 2.进入“百度地图开放平台”网站中&#xff0c;在导航中选择“”开发文档>坐标拾取器“”…

java后端工程师平时开发或多或少会用到eclipse,那么它有哪些快捷键呢

孙叫兽&#xff0c;前端全栈工程师&#xff0c;微信公众号&#xff1a;电商程序员&#xff0c;主页QQ群有eclipse安装包。 下载地址&#xff1a;https://www.eclipse.org/downloads/ Eclipse 是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言&#xff0c;它只是一个…

信息安全系统设计基础第六周学习总结

第六章 存储器层次结构 存储器系统 是一个具有不同容量、成本和访问时间的存储设备的层次结构。 1.CPU寄存器&#xff1a;容量小&#xff0c;成本高&#xff0c;访问快 2.高速缓存存储器&#xff1a;CPU和主存之间的缓存区域 3.主存&#xff1a;磁盘上大容量&#xff0c;成本低…

phpStudy + PhpStorm + XDebug调试【绝对能用】

具体参照的是这篇文章&#xff1a;https://blog.csdn.net/weixin_40418199/article/details/79088365 文章有些地方说的不是很详细想重写整理下。 【PHPStudy演示的版本为&#xff1a;PHP5.4.45-Apache】 1.PHPStudy配置 PHPStudy自带了XDebug的扩展&#xff0c;不需要下载 php…

亚马逊标题自动抓取_15分钟内开始使用Amazon Web Services和全自动资源调配

亚马逊标题自动抓取在等待一个新项目时&#xff0c;我想学习一些有用的东西。 而且由于在许多项目中我们需要评估和测试正在开发的应用程序的性能&#xff0c;而很少有足够的硬件来生成实际负载&#xff0c;因此我决定学习更多有关按需在云中按需配置虚拟机的知识&#xff0c;即…

java后端工程师平时开发或多或少会用到Intellij idea,那么它有哪些快捷键呢

孙叫兽,前端全栈工程师,微信公众号:电商程序员,主页QQ群有Intellij idea安装包及注册码。 下载地址:https://www.jetbrains.com/idea/download/ IDEA 全称 IntelliJ IDEA,是java编程语言开发的集成环境。IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、…

网络安全设备默认密码

因为触及敏感词v-p-n,需要将 ‘-’去掉。 设备默认账号默认密码深信服产品sangforsangfor sangfor2018 sangfor2019深信服科技 ADdlanrecover深信服负载均衡 AD 3.6adminadmin深信服WAC ( WNS V2.6)adminadmin深信服v-p-nAdminAdmin深信服ipsec-V-P-N (SSL 5.5)AdminAdmin深信…

Java注释处理器

本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的过程&#xff01; 在这里查看 &#xff01; 目录 1.简…

浅谈“PHP multipart/form-data 远程DOS漏洞”

一&#xff1a;漏洞名称&#xff1a; PHP multipart/form-data 远程DOS漏洞 描述&#xff1a; PHP 在处理HTTP请求中的multipart/form-data头部数据时存在一个安全漏洞&#xff0c;导致PHP大量重复分配和拷贝内存的操作&#xff0c;可能造成CPU资源占用100%并持续较长时间&…

java后端工程师平时开发或多或少会用到Myeclipse,那么它有哪些快捷键呢

孙叫兽,前端全栈工程师,微信公众号:电商程序员,主页QQ群有Myeclipse安装包。 下载地址:http://www.genuitec.com/products/myeclipse/download/ MyEclipse,是在Eclipse 基础上加上自己的插件开发而成的功能强大的企业级集成开发环境,主要用于Java、Java EE以及移动应用的…

加密编码类型的密文特征分析

转载https://blog.csdn.net/weixin_45728976/article/details/109219997 MD5、sha1、HMAC算法、NTLM等相似加密类型 1、MD5——示例21232F297A57A5A743894A0E4A801FC3 一般MD5值是32位由数字“0-9”和字母“a-f”所组成的字符串&#xff0c;如图。如果出现这个范围以外的字符…

孙叫兽进阶之路之如何进行情绪管理

程序员不光有硬实力&#xff0c;更要有软实力。 程序员文武双全之道。 ---孙叫兽&#xff0c;前端全栈程序员&#xff0c;微信公众号&#xff1a;电商程序员。 情绪的作用&#xff0c;存在于我们每天的工作和生活中&#xff0c;无时不刻地影响着人们的思想和行为。如何控制和管…

验证码(captcha)的由来

如果您允许用户在您的网站上发表内容&#xff0c;如留下评论和创建用户配置文件&#xff0c;那么您可能会看到&#xff0c;垃圾留言散播者试图利用这些渠道来给他们自己的网站创造流量。在您的网站上出现这类垃圾留言&#xff0c;对任何人来说都不愉快。用户可能遭遇恼人的广告…

javaone_JavaOne正在重建动力

javaone在JavaOne上度过了一个非常忙碌的一周&#xff0c;今年的活动有很多让人喜欢的地方。 有很多惊喜的公告&#xff0c;很多很好的内容/会议&#xff0c;并且在场地和组织上都有很多改进。 对于一直耐心等待我发表所有演讲的人们&#xff0c;我为您的延迟表示歉意……给4个…

夜神模拟器安装证书之burp抓包

安装步骤 1.http 不需要安装证书即可抓app包 2.https 需要安装证书 一、设置burp代理 注意IP是本机下的ip 二、模拟器设置代理 点击wifi长按鼠标修改网络 三、导出CA证书 模拟器中进入http://burp页面&#xff0c;点击黄色的地方下载 下载证书&#xff0c;可以选择本…

接手一个项目,后缀名为.bak文件,原来它是这个意思

.bak是备份文件&#xff0c;为文件格式扩展bai名&#xff0c;这类文件一du般在.bak前面加上应该有zhi原来的扩展名比如windows.dll.bak&#xff0c;或是windows_dll.bak&#xff0c;有dao的则是由原文件的后缀名和bak混合而成&#xff0c;如proteus的备份文件为.DBK。很多软件,…

java大佬是如何快速配置IntelliJ IDEA的Tomcat及安装配置Tomcat及java开发环境

孙叫兽,前端全栈工程师,java工程师。编译器及工具可以在主页QQ群群文件获取。 JDK 可以到官网下载:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html Tomcat下载:Http://tomcat.apache.org/ IntelliJ IDEA下载:https://www.jetbrain…