咱們先來討論一個問題,一個類的靜態變量當類被屢次實例化的時候,靜態變量是否會受影響?首先咱們應該清楚的是靜態變量是在類被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)); }
其結果依然不管運行多少次,都不會出現重複訂單號,由於這裏已經使用了類鎖來同步靜態變量。