靜態變量的多線程同步問題

咱們先來討論一個問題,一個類的靜態變量當類被屢次實例化的時候,靜態變量是否會受影響?首先咱們應該清楚的是靜態變量是在類被JVM classloader的時候分配內存,而且是分配在永久區而非堆內存中。安全

當咱們用對象鎖來同步靜態變量的時候,咱們來看一個例子。ide

public interface OrderService {
    public String getOrderNo();
}

先定義一個接口,獲取一個訂單編號。this

public class OrderLockServiceImpl implements OrderService {
    static int num = 0;
    @Override
    synchronized public String getOrderNo() {
        SimpleDateFormat date = new SimpleDateFormat("YYYYMMDDHHMMSS");
        return date.format(new Date()) + num++;
    }
}

實現這個接口,而且用對象方法來操做靜態變量。spa

public class OrderTask implements Runnable {
    private CountDownLatch latch;
    private OrderService orderService;
    public OrderTask(CountDownLatch latch,OrderService orderService) {
        this.latch = latch;
        this.orderService = orderService;
    }

    @Override
    public void run() {
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("線程名%s訂單號:%s\n",Thread.currentThread().getName(), orderService.getOrderNo());
    }
}

建立一個線程類,使用CountDownLatch來讓每個線程都有出現的機會,而不是某一個線程獨佔。線程

public static void main(String[] args) {
   ExecutorService service = Executors.newCachedThreadPool();
   final CountDownLatch latch = new CountDownLatch(1);
   for (int i = 0;i < 10;i++) {
      OrderService orderService = new OrderLockServiceImpl();
      service.submit(new OrderTask(latch,orderService));
   }
   latch.countDown();
   service.shutdown();
}

最後實現main方法,用線程池來提交10次線程任務。結果咱們發現,這個靜態變量會出現重複(並非每一次運行都會重複)orm

線程名pool-2-thread-1訂單號:20180719619072331
線程名pool-2-thread-7訂單號:20180719619072337
線程名pool-2-thread-3訂單號:20180719619072337
線程名pool-2-thread-9訂單號:20180719619072336
線程名pool-2-thread-5訂單號:20180719619072335
線程名pool-2-thread-6訂單號:20180719619072334
線程名pool-2-thread-10訂單號:20180719619072333
線程名pool-2-thread-4訂單號:20180719619072330
線程名pool-2-thread-2訂單號:20180719619072332
線程名pool-2-thread-8訂單號:20180719619072338對象

由此咱們能夠判定,對象鎖並不能保證靜態變量的同步安全。不過若是對象在堆內存中是惟一的,如繼承

public static void main(String[] args) {
   ExecutorService service = Executors.newCachedThreadPool();
   final CountDownLatch latch = new CountDownLatch(1);
   OrderService orderService = new OrderLockServiceImpl();
   for (int i = 0;i < 10;i++) {
      service.submit(new OrderTask(latch,orderService));
   }
   latch.countDown();
   service.shutdown();
}

則不管你運行多少次,則靜態變量也必定是同步的。接口

可是若是咱們使用的是類鎖,來改進上面的代碼。內存

public abstract class AbstractOrderService implements OrderService {
    static int num = 0;
    public static synchronized String getOrderNo2() {
        SimpleDateFormat date = new SimpleDateFormat("YYYYMMDDHHMMSS");
        return date.format(new Date()) + num++;
    }
    @Override
    public String getOrderNo() {
        return getOrderNo2();
    }
}

抽象類中使用靜態方法,來同步靜態變量。

public class OrderLockServiceImpl extends AbstractOrderService {
    
}

咱們在子類中什麼都不作,只繼承於抽象類

此時咱們即使實例化10個對象

for (int i = 0;i < 10;i++) {
   OrderService orderService = new OrderLockServiceImpl();
   service.submit(new OrderTask(latch,orderService));
}

其結果依然不管運行多少次,都不會出現重複訂單號,由於這裏已經使用了類鎖來同步靜態變量。

相關文章
相關標籤/搜索