更新時(shí)間:2023-07-25 來(lái)源:黑馬程序員 瀏覽量:
在Java中,實(shí)現(xiàn)可見(jiàn)性(visibility)的主要方法是使用關(guān)鍵字volatile和使用鎖(如synchronized關(guān)鍵字或 java.util.concurrent包中的鎖)來(lái)確保對(duì)共享變量的修改在多線程環(huán)境中能夠正確地被其他線程所觀察到。
下面詳細(xì)說(shuō)明這兩種方法,并附帶代碼演示。
volatile是一種輕量級(jí)的同步機(jī)制,用于告訴JVM對(duì)被修飾的變量不進(jìn)行緩存,直接從主內(nèi)存中讀取和寫(xiě)入數(shù)據(jù)。這樣可以確保當(dāng)一個(gè)線程修改了變量的值時(shí),其他線程能夠立即看到這個(gè)變化,而不是使用緩存中的舊值。
public class VolatileVisibilityExample { private volatile boolean flag = false; public void setFlag(boolean value) { flag = value; } public void checkFlag() { while (!flag) { // Busy waiting until the flag becomes true } System.out.println("Flag is now true!"); } public static void main(String[] args) { VolatileVisibilityExample example = new VolatileVisibilityExample(); new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } example.setFlag(true); }).start(); new Thread(() -> { example.checkFlag(); }).start(); } }
在上面的例子中,我們創(chuàng)建了一個(gè)VolatileVisibilityExample類(lèi),其中的flag變量被聲明為volatile。第一個(gè)線程在啟動(dòng)后會(huì)等待1秒鐘然后將flag設(shè)置為true,而第二個(gè)線程在flag變?yōu)閠rue之前會(huì)一直進(jìn)行忙等待(busy waiting)。由于flag是volatile的,第二個(gè)線程能夠看到第一個(gè)線程對(duì)flag的修改,并在flag變?yōu)閠rue時(shí)結(jié)束忙等待。
另一種實(shí)現(xiàn)可見(jiàn)性的方法是使用鎖,通過(guò)synchronized關(guān)鍵字或者java.util.concurrent包中的鎖機(jī)制來(lái)保護(hù)對(duì)共享變量的訪問(wèn)。
public class LockVisibilityExample { private boolean flag = false; private final Object lock = new Object(); public void setFlag(boolean value) { synchronized (lock) { flag = value; } } public void checkFlag() { synchronized (lock) { while (!flag) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("Flag is now true!"); } public static void main(String[] args) { LockVisibilityExample example = new LockVisibilityExample(); new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } example.setFlag(true); synchronized (example.lock) { example.lock.notifyAll(); } }).start(); new Thread(() -> { example.checkFlag(); }).start(); } }
在上面的例子中,我們創(chuàng)建了一個(gè)LockVisibilityExample類(lèi),其中使用了一個(gè)名為lock的對(duì)象作為鎖。在setFlag和checkFlag方法中,我們使用synchronized關(guān)鍵字來(lái)保護(hù)對(duì)flag的訪問(wèn)。第一個(gè)線程設(shè)置flag為true并通過(guò)synchronized塊的notifyAll()方法通知第二個(gè)線程,而第二個(gè)線程則在flag變?yōu)閠rue之前一直等待,并在被通知后結(jié)束等待。
這兩種方法都可以實(shí)現(xiàn)可見(jiàn)性,但使用volatile更為簡(jiǎn)單和輕量級(jí)。然而,在某些情況下,使用鎖可能會(huì)更有優(yōu)勢(shì),例如需要進(jìn)行更復(fù)雜的操作或需要更精細(xì)地控制對(duì)共享資源的訪問(wèn)。