2019獨角獸企業重金招聘Python工程師標準>>>
我們先來討論一個問題,一個類的靜態變量當類被多次實例化的時候,靜態變量是否會受影響?首先我們應該清楚的是靜態變量是在類被JVM classloader的時候分配內存,并且是分配在永久區而非堆內存中。
當我們用對象鎖來同步靜態變量的時候,我們來看一個例子。
public interface OrderService {public String getOrderNo(); }
先定義一個接口,獲取一個訂單編號。
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++; } }
實現這個接口,并且用對象方法來操作靜態變量。
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次線程任務。結果我們發現,這個靜態變量會出現重復(并不是每一次運行都會重復)
線程名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)); }
其結果依然無論運行多少次,都不會出現重復訂單號,因為這里已經使用了類鎖來同步靜態變量。