日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
面試突擊:線(xiàn)程安全問(wèn)題的解決方案有哪些?
  • 使用線(xiàn)程安全類(lèi),比如 AtomicInteger。
  • 加鎖排隊(duì)執(zhí)行

使用 synchronized 加鎖。

使用 ReentrantLock 加鎖。

  • 使用線(xiàn)程本地變量 ThreadLocal。

接下來(lái)我們逐個(gè)來(lái)看它們的實(shí)現(xiàn)。

線(xiàn)程安全問(wèn)題

演示我們創(chuàng)建一個(gè)變量 number 等于 0,之后創(chuàng)建線(xiàn)程 1,執(zhí)行 100 萬(wàn)次 ++ 操作,同時(shí)再創(chuàng)建線(xiàn)程 2 執(zhí)行 100 萬(wàn)次 -- 操作,等線(xiàn)程 1 和線(xiàn)程 2 都執(zhí)行完之后,打印 number 變量的值,如果打印的結(jié)果為 0,則說(shuō)明是線(xiàn)程安全的,否則則為非線(xiàn)程安全的,示例代碼如下:

public class ThreadSafeTest {
// 全局變量
private static int number = 0;
// 循環(huán)次數(shù)(100W)
private static final int COUNT = 1_000_000;

public static void main(String[] args) throws InterruptedException {
// 線(xiàn)程1:執(zhí)行 100W 次 ++ 操作
Thread t1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
number++;
}
});
t1.start();

// 線(xiàn)程2:執(zhí)行 100W 次 -- 操作
Thread t2 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
number--;
}
});
t2.start();

// 等待線(xiàn)程 1 和線(xiàn)程 2,執(zhí)行完,打印 number 最終的結(jié)果
t1.join();
t2.join();
System.out.println("number 最終結(jié)果:" + number);
}
}

以上程序的執(zhí)行結(jié)果如下圖所示:

從上述執(zhí)行結(jié)果可以看出,number 變量最終的結(jié)果并不是 0,和預(yù)期的正確結(jié)果不相符,這就是多線(xiàn)程中的線(xiàn)程安全問(wèn)題。

解決線(xiàn)程安全問(wèn)題

1.原子類(lèi)AtomicIntege

AtomicInteger 是線(xiàn)程安全的類(lèi),使用它可以將 ++ 操作和 -- 操作,變成一個(gè)原子性操作,這樣就能解決非線(xiàn)程安全的問(wèn)題了,如下代碼所示:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerExample {
// 創(chuàng)建 AtomicInteger
private static AtomicInteger number = new AtomicInteger(0);
// 循環(huán)次數(shù)
private static final int COUNT = 1_000_000;

public static void main(String[] args) throws InterruptedException {
// 線(xiàn)程1:執(zhí)行 100W 次 ++ 操作
Thread t1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
// ++ 操作
number.incrementAndGet();
}
});
t1.start();

// 線(xiàn)程2:執(zhí)行 100W 次 -- 操作
Thread t2 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
// -- 操作
number.decrementAndGet();
}
});
t2.start();

// 等待線(xiàn)程 1 和線(xiàn)程 2,執(zhí)行完,打印 number 最終的結(jié)果
t1.join();
t2.join();
System.out.println("最終結(jié)果:" + number.get());
}
}

以上程序的執(zhí)行結(jié)果如下圖所示:

2.加鎖排隊(duì)執(zhí)行

Java 中有兩種鎖:synchronized 同步鎖和 ReentrantLock 可重入鎖。

2.1 同步鎖synchronized

synchronized 是 JVM 層面實(shí)現(xiàn)的自動(dòng)加鎖和自動(dòng)釋放鎖的同步鎖,它的實(shí)現(xiàn)代碼如下:

public class SynchronizedExample {
// 全局變量
private static int number = 0;
// 循環(huán)次數(shù)(100W)
private static final int COUNT = 1_000_000;

public static void main(String[] args) throws InterruptedException {
// 線(xiàn)程1:執(zhí)行 100W 次 ++ 操作
Thread t1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
// 加鎖排隊(duì)執(zhí)行
synchronized (SynchronizedExample.class) {
number++;
}
}
});
t1.start();

// 線(xiàn)程2:執(zhí)行 100W 次 -- 操作
Thread t2 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
// 加鎖排隊(duì)執(zhí)行
synchronized (SynchronizedExample.class) {
number--;
}
}
});
t2.start();

// 等待線(xiàn)程 1 和線(xiàn)程 2,執(zhí)行完,打印 number 最終的結(jié)果
t1.join();
t2.join();
System.out.println("number 最終結(jié)果:" + number);
}
}

以上程序的執(zhí)行結(jié)果如下圖所示:

2.2 可重入鎖ReentrantLock

ReentrantLock 可重入鎖需要程序員自己加鎖和釋放鎖,它的實(shí)現(xiàn)代碼如下:

import java.util.concurrent.locks.ReentrantLock;

/**
* 使用 ReentrantLock 解決非線(xiàn)程安全問(wèn)題
*/
public class ReentrantLockExample {
// 全局變量
private static int number = 0;
// 循環(huán)次數(shù)(100W)
private static final int COUNT = 1_000_000;
// 創(chuàng)建 ReentrantLock
private static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) throws InterruptedException {
// 線(xiàn)程1:執(zhí)行 100W 次 ++ 操作
Thread t1 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
lock.lock(); // 手動(dòng)加鎖
number++; // ++ 操作
lock.unlock(); // 手動(dòng)釋放鎖
}
});
t1.start();

// 線(xiàn)程2:執(zhí)行 100W 次 -- 操作
Thread t2 = new Thread(() -> {
for (int i = 0; i < COUNT; i++) {
lock.lock(); // 手動(dòng)加鎖
number--; // -- 操作
lock.unlock(); // 手動(dòng)釋放鎖
}
});
t2.start();

// 等待線(xiàn)程 1 和線(xiàn)程 2,執(zhí)行完,打印 number 最終的結(jié)果
t1.join();
t2.join();
System.out.println("number 最終結(jié)果:" + number);
}
}

以上程序的執(zhí)行結(jié)果如下圖所示:

3.線(xiàn)程本地變量ThreadLocal

使用 ThreadLocal 線(xiàn)程本地變量也可以解決線(xiàn)程安全問(wèn)題,它是給每個(gè)線(xiàn)程獨(dú)自創(chuàng)建了一份屬于自己的私有變量,不同的線(xiàn)程操作的是不同的變量,所以也不會(huì)存在非線(xiàn)程安全的問(wèn)題,它的實(shí)現(xiàn)代碼如下:

public class ThreadSafeExample {
// 創(chuàng)建 ThreadLocal(設(shè)置每個(gè)線(xiàn)程中的初始值為 0)
private static ThreadLocal threadLocal = ThreadLocal.withInitial(() -> 0);
// 全局變量
private static int number = 0;
// 循環(huán)次數(shù)(100W)
private static final int COUNT = 1_000_000;

public static void main(String[] args) throws InterruptedException {
// 線(xiàn)程1:執(zhí)行 100W 次 ++ 操作
Thread t1 = new Thread(() -> {
try {
for (int i = 0; i < COUNT; i++) {
// ++ 操作
threadLocal.set(threadLocal.get() + 1);
}
// 將 ThreadLocal 中的值進(jìn)行累加
number += threadLocal.get();
} finally {
threadLocal.remove(); // 清除資源,防止內(nèi)存溢出
}
});
t1.start();

// 線(xiàn)程2:執(zhí)行 100W 次 -- 操作
Thread t2 = new Thread(() -> {
try {
for (int i = 0; i < COUNT; i++) {
// -- 操作
threadLocal.set(threadLocal.get() - 1);
}
// 將 ThreadLocal 中的值進(jìn)行累加
number += threadLocal.get();
} finally {
threadLocal.remove(); // 清除資源,防止內(nèi)存溢出
}
});
t2.start();

// 等待線(xiàn)程 1 和線(xiàn)程 2,執(zhí)行完,打印 number 最終的結(jié)果
t1.join();
t2.join();
System.out.println("最終結(jié)果:" + number);
}
}

以上程序的執(zhí)行結(jié)果如下圖所示:

總結(jié)

在 Java 中,解決線(xiàn)程安全問(wèn)題的手段有 3 種:

1.使用線(xiàn)程安全的類(lèi),如 AtomicInteger 類(lèi);

2.使用鎖 synchronized 或 ReentrantLock 加鎖排隊(duì)執(zhí)行;

3.使用線(xiàn)程本地變量 ThreadLocal 來(lái)處理。


當(dāng)前文章:面試突擊:線(xiàn)程安全問(wèn)題的解決方案有哪些?
本文鏈接:http://www.dlmjj.cn/article/dhooegj.html