赞
踩
目录
线程安全是指某个方法或某段代码,在多线程中能够正确的执行,不会出现数据不一致或数据污染的情况,我们把这样的程序称之为线程安全的,反之则为非线程安全的。
如果是多线程同时修改不同的变量(每个线程只修改自己的变量),也是不会出现非线程安全的问题了,比如以下代码,线程 1 修改 number1 变量,而线程 2 修改 number2 变量,最终两个线程执行完之后的结果如下:
- public class test2{
- // 全局变量
- private static int number = 0;
- // 循环次数(100W)
- private static final int COUNT = 1_000_000;
- // 线程 1 操作的变量 number1
- private static int number1 = 0;
- // 线程 2 操作的变量 number2
- private static int number2 = 0;
-
- public static void main(String[] args) throws InterruptedException {
- // 线程1:执行 100W 次 number+1 操作
- Thread t1 = new Thread(() -> {
- for (int i = 0; i < COUNT; i++) {
- number1++;
- }
- });
- t1.start();
-
- // 线程2:执行 100W 次 number-1 操作
- Thread t2 = new Thread(() -> {
- for (int i = 0; i < COUNT; i++) {
- number2--;
- }
- });
- t2.start();
-
- // 等待线程 1 和线程 2,执行完,打印 number 最终的结果
- t1.join();
- t2.join();
- number = number1 + number2;
- System.out.println("number=number1+number2 最终结果:" + number);
- }
- }
程序的运行结果:
从上述结果可以看出,多线程只要不是同时修改同一个变量,也不会出现线程安全问题。
原子性操作是指操作不能再被分隔就叫原子性操作。首先,要明确java代码中的一条语句可能代表多个指令,例如 r++ 、r--都是对应多条指令,所以在这里他们的原子性没有得到保证。
以上就是一个经典的错误,r 原本等于 1,线程 1 进行 -1 操作,而线程 2 进行加 1,最终的结果 n,r 应该还等于 1 才对,但通过上面的执行,number 最终被修改成了 0,这就是非原子性导致的问题。
在 Java 编程中内存分为两种类型:工作内存和主内存,而工作内存使用的是 CPU 寄存器实现的,而主内存是指电脑中的内存,我们知道 CPU 寄存器的操作速度是远大于内存的操作速度的,它们的性能差异如下图所示:
在 Java 语言中,为了提高程序的执行速度,所以在操作变量时,会将变量从主内存中复制一份到工作内存,而主内存是所有线程共用的,工作内存是每个线程私有的,这就会导致一个线程已经把主内存中的公共变量修改了,而另一个线程不知道,依旧使用自己工作内存中的变量,这样就导致了问题的产生,也就导致了线程安全问题。
例如在实例化对象的时候:
SomeObeject so = new SomeObject();
上述代码可以分为三步:
1.根据类计算对象的大小:在堆内存中分配内存空间给该对象。
2.对对象进行初始化(构造代码块、构造方法)。
3.把引用交给 so。
但是由于代码重排序的原因,将本来的顺序变为 1 -> 3 ->2 ;假设在执行到 3的时候,发生了线程调度,假如此时新来的线程需要 so对象,所以就使用了,但是,因为代码重排序的问题,上一个线程还没有进行对象的初始化,所以导致发生错误。
Java 中有两种锁:synchronized 同步锁和 ReentrantLock 可重入锁。
synchronized 是 JVM 层面实现的自动加锁和自动释放锁的同步锁,它的实现代码如下:
- public class Test2 {
- // 全局变量
- 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 (Test2.class) {
- number++;
- }
- }
- });
- t1.start();
-
- // 线程2:执行 100W 次 -- 操作
- Thread t2 = new Thread(() -> {
- for (int i = 0; i < COUNT; i++) {
- // 加锁排队执行
- synchronized (Test2.class) {
- number--;
- }
- }
- });
- t2.start();
-
- // 等待线程 1 和线程 2,执行完,打印 number 最终的结果
- t1.join();
- t2.join();
- System.out.println("number 最终结果:" + number);
- }
- }
运行结果如下:
ReentrantLock 可重入锁需要程序员自己加锁和释放锁,它的实现代码如下:
- public class Test2{
- // 全局变量
- 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);
- }
-
- }
-
-
程序运行结果如下:
AtomicInteger 是线程安全的类,使用它可以将 ++ 操作和 -- 操作,变成一个原子性操作,这样就能解决非线程安全的问题了,如下代码所示:
- public class Test2 {
- // 创建 AtomicInteger
- private 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 最终结果:" + number.get());
- }
- }
程序的运行结果:
加油偶~~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。