产品review网站怎么做,现在有哪家建筑公司招人,想把一个网站屏蔽了怎么做,个体工商户可以做网站吗作者 | 磊哥来源 | Java面试真题解析#xff08;ID#xff1a;aimianshi666#xff09;转载请联系授权#xff08;微信ID#xff1a;GG_Stone#xff09;线程安全是指某个方法或某段代码#xff0c;在多线程中能够正确的执行#xff0c;不会出现数据不一致或数据污染的… 作者 | 磊哥来源 | Java面试真题解析IDaimianshi666转载请联系授权微信IDGG_Stone线程安全是指某个方法或某段代码在多线程中能够正确的执行不会出现数据不一致或数据污染的情况我们把这样的程序称之为线程安全的反之则为非线程安全的。在 Java 中解决线程安全问题有以下 3 种手段使用线程安全类比如 AtomicInteger。加锁排队执行使用 synchronized 加锁。使用 ReentrantLock 加锁。使用线程本地变量 ThreadLocal。接下来我们逐个来看它们的实现。线程安全问题演示我们创建一个变量 number 等于 0之后创建线程 1执行 100 万次 操作同时再创建线程 2 执行 100 万次 -- 操作等线程 1 和线程 2 都执行完之后打印 number 变量的值如果打印的结果为 0则说明是线程安全的否则则为非线程安全的示例代码如下public class ThreadSafeTest {// 全局变量private static int number 0;// 循环次数(100W)private static final int COUNT 1_000_000;public static void main(String[] args) throws InterruptedException {// 线程1执行 100W 次 操作Thread t1 new Thread(() - {for (int i 0; i COUNT; i) {number;}});t1.start();// 线程2执行 100W 次 -- 操作Thread t2 new Thread(() - {for (int i 0; i COUNT; i) {number--;}});t2.start();// 等待线程 1 和线程 2执行完打印 number 最终的结果t1.join();t2.join();System.out.println(number 最终结果 number);}
}以上程序的执行结果如下图所示从上述执行结果可以看出number 变量最终的结果并不是 0和预期的正确结果不相符这就是多线程中的线程安全问题。解决线程安全问题1.原子类AtomicIntegerAtomicInteger 是线程安全的类使用它可以将 操作和 -- 操作变成一个原子性操作这样就能解决非线程安全的问题了如下代码所示import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerExample {// 创建 AtomicIntegerprivate static AtomicInteger number new AtomicInteger(0);// 循环次数private static final int COUNT 1_000_000;public static void main(String[] args) throws InterruptedException {// 线程1执行 100W 次 操作Thread t1 new Thread(() - {for (int i 0; i COUNT; i) {// 操作number.incrementAndGet();}});t1.start();// 线程2执行 100W 次 -- 操作Thread t2 new Thread(() - {for (int i 0; i COUNT; i) {// -- 操作number.decrementAndGet();}});t2.start();// 等待线程 1 和线程 2执行完打印 number 最终的结果t1.join();t2.join();System.out.println(最终结果 number.get());}
}以上程序的执行结果如下图所示2.加锁排队执行Java 中有两种锁synchronized 同步锁和 ReentrantLock 可重入锁。2.1 同步锁synchronizedsynchronized 是 JVM 层面实现的自动加锁和自动释放锁的同步锁它的实现代码如下public class SynchronizedExample {// 全局变量private static int number 0;// 循环次数(100W)private static final int COUNT 1_000_000;public static void main(String[] args) throws InterruptedException {// 线程1执行 100W 次 操作Thread t1 new Thread(() - {for (int i 0; i COUNT; i) {// 加锁排队执行synchronized (SynchronizedExample.class) {number;}}});t1.start();// 线程2执行 100W 次 -- 操作Thread t2 new Thread(() - {for (int i 0; i COUNT; i) {// 加锁排队执行synchronized (SynchronizedExample.class) {number--;}}});t2.start();// 等待线程 1 和线程 2执行完打印 number 最终的结果t1.join();t2.join();System.out.println(number 最终结果 number);}
}以上程序的执行结果如下图所示2.2 可重入锁ReentrantLockReentrantLock 可重入锁需要程序员自己加锁和释放锁它的实现代码如下import java.util.concurrent.locks.ReentrantLock;/*** 使用 ReentrantLock 解决非线程安全问题*/
public class ReentrantLockExample {// 全局变量private static int number 0;// 循环次数(100W)private static final int COUNT 1_000_000;// 创建 ReentrantLockprivate static ReentrantLock lock new ReentrantLock();public static void main(String[] args) throws InterruptedException {// 线程1执行 100W 次 操作Thread t1 new Thread(() - {for (int i 0; i COUNT; i) {lock.lock(); // 手动加锁number; // 操作lock.unlock(); // 手动释放锁}});t1.start();// 线程2执行 100W 次 -- 操作Thread t2 new Thread(() - {for (int i 0; i COUNT; i) {lock.lock(); // 手动加锁number--; // -- 操作lock.unlock(); // 手动释放锁}});t2.start();// 等待线程 1 和线程 2执行完打印 number 最终的结果t1.join();t2.join();System.out.println(number 最终结果 number);}
}以上程序的执行结果如下图所示3.线程本地变量ThreadLocal使用 ThreadLocal 线程本地变量也可以解决线程安全问题它是给每个线程独自创建了一份属于自己的私有变量不同的线程操作的是不同的变量所以也不会存在非线程安全的问题它的实现代码如下public class ThreadSafeExample {// 创建 ThreadLocal设置每个线程中的初始值为 0private static ThreadLocalInteger threadLocal ThreadLocal.withInitial(() - 0);// 全局变量private static int number 0;// 循环次数(100W)private static final int COUNT 1_000_000;public static void main(String[] args) throws InterruptedException {// 线程1执行 100W 次 操作Thread t1 new Thread(() - {try {for (int i 0; i COUNT; i) {// 操作threadLocal.set(threadLocal.get() 1);}// 将 ThreadLocal 中的值进行累加number threadLocal.get();} finally {threadLocal.remove(); // 清除资源防止内存溢出}});t1.start();// 线程2执行 100W 次 -- 操作Thread t2 new Thread(() - {try {for (int i 0; i COUNT; i) {// -- 操作threadLocal.set(threadLocal.get() - 1);}// 将 ThreadLocal 中的值进行累加number threadLocal.get();} finally {threadLocal.remove(); // 清除资源防止内存溢出}});t2.start();// 等待线程 1 和线程 2执行完打印 number 最终的结果t1.join();t2.join();System.out.println(最终结果 number);}
}以上程序的执行结果如下图所示总结在 Java 中解决线程安全问题的手段有 3 种1.使用线程安全的类如 AtomicInteger 类2.使用锁 synchronized 或 ReentrantLock 加锁排队执行3.使用线程本地变量 ThreadLocal 来处理。是非审之于己毁誉听之于人得失安之于数。公众号Java面试真题解析面试合集https://gitee.com/mydb/interview往期推荐面试突击36线程安全问题是如何产生的每周汇总 | Java面试题共35篇2022版面试突击35如何判断线程池已经执行完所有任务了