Java线程安全问题,java线程不安全详解
00-1010线程安全问题演示解决线程安全问题1。原子类AtomicInteger2。锁定队列执行2.1同步锁synchronized2.2 ReentrantLock3。ThreadLocal变量threadlocal摘要前言:
线程安全意味着一个方法或一段代码可以在多线程中正确执行,不会出现数据不一致或数据污染。我们称这样的程序为线程安全的,反之亦然。在Java中,
解决线程安全问题有以下 3 种手段:
使用线程安全的类,比如AtomicInteger。锁定队列执行使用同步锁定。使用ReentrantLock进行锁定。使用ThreadLocal变量threadlocal。接下来,我们来逐一看看它们的实现。
00-1010我们创建一个变量数等于0,然后创建线程1,执行100万次操作,然后创建线程2,执行100万次操作。线程1和线程2都执行完之后,打印number变量的值。如果打印结果为0,说明是线程安全的,否则是非线程安全的。
示例代码如下:
类线程安全测试{//全局变量私有静态int number=0;//周期数(100w)私有静态最终int count=1 _ 000 _ 000公共静态void main (string [] args)引发中断的异常{//Thread 1:执行100W操作Thread t1=new Thread(()-{ for(int I=0;我数;i ) {数字;} });t1 . start();//线程2:执行100W次-操作Thread T2=new Thread(()-{ for(int I=0;我数;i ) {数字-;} });T2 . start();//等待线程1和线程2。执行后,打印number的最终结果t1 . join();T2 . join();System.out.println(number最终结果: number );} }以上程序的执行结果如下图所示:
从上面的执行结果可以看出,number变量的最终结果不是0,与预期的正确结果不一致。这就是多线程中的线程安全问题。
目录
线程安全问题演示
AtomicInteger是一个线程安全的类。使用它,可以把操作和-操作变成原子操作,从而解决非线程安全问题。
如下代码所示:
导入Java . util . concurrent . atomic . atomic integer;类原子性整数示例{//Create atomicity integer private static atomicity integer number=new atomicity integer(0);//周期数私有静态最终int count=1 _ 000 _ 000公共静态void main (string [] args)引发中断的异常{//Thread 1:执行100W操作Thread t1=new Thread(()-{ for(int I=0;我数;I){//operation 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 同步锁synchronized
synchronized 是 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 可重入锁ReentrantLock
ReentrantLock 可重入锁需要程序员自己加锁和释放锁,它的实现代码如下:
import java.util.concurrent.locks.ReentrantLock;/** * 使用 ReentrantLock 解决非线程安全问题 */public class ReentrantLockExample { // 全局变量 private static int number = 0; // 循环次数(100W) private static final int COUNT = 1_000_000; // 创建 ReentrantLock private 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(设置每个线程中的初始值为 0) private static ThreadLocal<Integer> 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线程安全问题的解决方案的文章就介绍到这了,更多相关Java线程安全内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。