最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java的volatile和sychronized底層實(shí)現(xiàn)原理解析

 更新時(shí)間:2025年03月15日 08:58:44   作者:songtianer  
文章詳細(xì)介紹了Java中的synchronized和volatile關(guān)鍵字的底層實(shí)現(xiàn)原理,包括字節(jié)碼層面、JVM層面的實(shí)現(xiàn)細(xì)節(jié),以及鎖的類(lèi)型和MESI協(xié)議在多核處理器中的作用,文章還探討了synchronized和volatile的區(qū)別,以及如何通過(guò)Atomic類(lèi)來(lái)實(shí)現(xiàn)更細(xì)粒度的原子操作,感興趣的朋友一起看看吧

1. 概覽

從Java代碼級(jí)別到硬件級(jí)別各層都是如何實(shí)現(xiàn)的

2. Synchronized

2.1 字節(jié)碼層面

使用javap -verbose <class文件>可以查看到字節(jié)碼信息,其中synchronized方法會(huì)有flags:ACC_SYNCHRONIZED,此時(shí)字節(jié)碼中不會(huì)包含monitorenter和moniotrexit,JVM會(huì)自動(dòng)加

public synchronized void syncMethod();
  flags: ACC_PUBLIC, ACC_SYNCHRONIZED

使用``javap -verbose <class文件>`編譯一個(gè)帶synchronized塊的代碼可以看到字節(jié)碼中的monitorenter和moniotrexit

0: new #2                  // 創(chuàng)建一個(gè)新的Object實(shí)例
3: dup
4: invokespecial #1        // 調(diào)用Object的構(gòu)造函數(shù)
7: astore_1                // 將引用存儲(chǔ)到局部變量1(lock)
8: aload_1                 // 將局部變量1(lock)加載到操作數(shù)棧
9: monitorenter            // 進(jìn)入monitor
10: ...                    // 同步塊體的字節(jié)碼
   : aload_1
   : monitorexit           // 退出monitor
   : ...

2.2 JVM層面

源碼可以在Github上面查看

monitorenter底層是由JVM的代碼ObjectMonitor來(lái)實(shí)現(xiàn)的

ObjectMonitor() {
    // 多線(xiàn)程競(jìng)爭(zhēng)鎖進(jìn)入時(shí)的單向鏈表
    ObjectWaiter * volatile _cxq;
    //處于等待鎖block狀態(tài)的線(xiàn)程,會(huì)被加入到該列表
    ObjectWaiter * volatile _EntryList;
    // _header是一個(gè)markOop類(lèi)型,markOop就是對(duì)象頭中的Mark Word
    volatile markOop _header;
    // 搶占該鎖的線(xiàn)程數(shù),約等于WaitSet.size + EntryList.size
    volatile intptr_t _count;
    // 等待線(xiàn)程數(shù)
    volatile intptr_t _waiters;
    // 鎖的重入次數(shù)
    volatile intptr_ _recursions;
    // 監(jiān)視器鎖寄生的對(duì)象,鎖是寄托存儲(chǔ)于對(duì)象中
    void* volatile  _object;
    // 指向持有ObjectMonitor對(duì)象的線(xiàn)程
    void* volatile _owner;
    // 處于wait狀態(tài)的線(xiàn)程,會(huì)被加入到_WaitSet
    ObjectWaiter * volatile _WaitSet;
    // 操作WaitSet鏈表的鎖
    volatile int _WaitSetLock;
    // 嵌套加鎖次數(shù),最外層鎖的_recursions屬性為0
    volatile intptr_t  _recursions;
}

2.2.1 enter方法

整個(gè)方法比較長(zhǎng),但我們了解的無(wú)鎖、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖都可以看到,核心方法是Atomic::cmpxchg_ptr,這個(gè)是CAS操作

方法描述
偏向鎖Atomic::cmpxchg_ptr將owner替換為當(dāng)前線(xiàn)程,成功則獲取到鎖
輕量級(jí)鎖TrySpin->Atomic::cmpxchg_ptr不斷自旋將owner替換為當(dāng)前線(xiàn)程,成功則獲取到鎖
重量級(jí)鎖EnterI>Atomic::cmpxchg_ptrpark然后將owner替換為當(dāng)前線(xiàn)程,成功則獲取到鎖
void ATTR ObjectMonitor::enter(TRAPS) {
  // The following code is ordered to check the most common cases first
  // and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
  Thread * const Self = THREAD ;
  void * cur ;
  // 無(wú)鎖CAS 轉(zhuǎn)為 偏向鎖
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  if (cur == NULL) {
     // Either ASSERT _recursions == 0 or explicitly set _recursions = 0.
     assert (_recursions == 0   , "invariant") ;
     assert (_owner      == Self, "invariant") ;
     // CONSIDER: set or assert OwnerIsThread == 1
     return ;
  }
	// 可重入鎖
  if (cur == Self) {
     // TODO-FIXME: check for integer overflow!  BUGID 6557169.
     _recursions ++ ;
     return ;
  }
  if (Self->is_lock_owned ((address)cur)) {
    assert (_recursions == 0, "internal state error");
    _recursions = 1 ;
    // Commute owner from a thread-specific on-stack BasicLockObject address to
    // a full-fledged "Thread *".
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }
  // We've encountered genuine contention.
  assert (Self->_Stalled == 0, "invariant") ;
  Self->_Stalled = intptr_t(this) ;
  // Try one round of spinning *before* enqueueing Self
  // and before going through the awkward and expensive state
  // transitions.  The following spin is strictly optional ...
  // Note that if we acquire the monitor from an initial spin
  // we forgo posting JVMTI events and firing DTRACE probes.
  // 自旋獲取鎖
  if (Knob_SpinEarly && TrySpin (Self) > 0) {
     assert (_owner == Self      , "invariant") ;
     assert (_recursions == 0    , "invariant") ;
     assert (((oop)(object()))->mark() == markOopDesc::encode(this), "invariant") ;
     Self->_Stalled = 0 ;
     return ;
  }
  assert (_owner != Self          , "invariant") ;
  assert (_succ  != Self          , "invariant") ;
  assert (Self->is_Java_thread()  , "invariant") ;
  JavaThread * jt = (JavaThread *) Self ;
  assert (!SafepointSynchronize::is_at_safepoint(), "invariant") ;
  assert (jt->thread_state() != _thread_blocked   , "invariant") ;
  assert (this->object() != NULL  , "invariant") ;
  assert (_count >= 0, "invariant") ;
  // Prevent deflation at STW-time.  See deflate_idle_monitors() and is_busy().
  // Ensure the object-monitor relationship remains stable while there's contention.
  Atomic::inc_ptr(&_count);
  EventJavaMonitorEnter event;
  { // Change java thread status to indicate blocked on monitor enter.
    JavaThreadBlockedOnMonitorEnterState jtbmes(jt, this);
    DTRACE_MONITOR_PROBE(contended__enter, this, object(), jt);
    if (JvmtiExport::should_post_monitor_contended_enter()) {
      JvmtiExport::post_monitor_contended_enter(jt, this);
    }
    OSThreadContendState osts(Self->osthread());
    ThreadBlockInVM tbivm(jt);
    Self->set_current_pending_monitor(this);
    // TODO-FIXME: change the following for(;;) loop to straight-line code.
    for (;;) {
      jt->set_suspend_equivalent();
      // cleared by handle_special_suspend_equivalent_condition()
      // or java_suspend_self()
			// 重量級(jí)鎖
      EnterI (THREAD) ;
    省略.......
}

2.2.2 cmpxchg_ptr

上面的鎖都用了這個(gè)方法cmpxchg_ptr,這個(gè)和java中的cas是類(lèi)似的,那它又是怎么實(shí)現(xiàn)的呢

atomic源碼

其中cmpxchg是Linux操作系統(tǒng)的函數(shù),執(zhí)行了一段匯編指令,并且有l(wèi)ock前綴

// 多核心多cpu前面就要加lock
#define LOCK_IF_MP(mp) "cmp $0, " #mp "; je 1f; lock; 1: "
inline intptr_t Atomic::cmpxchg_ptr(intptr_t exchange_value, volatile intptr_t* dest, intptr_t compare_value) {
  return (intptr_t)cmpxchg((jlong)exchange_value, (volatile jlong*)dest, (jlong)compare_value);
}
inline jlong    Atomic::cmpxchg    (jlong    exchange_value, volatile jlong*    dest, jlong    compare_value) {
  bool mp = os::is_MP();
  __asm__ __volatile__ (LOCK_IF_MP(%4) "cmpxchgq %1,(%3)"
                        : "=a" (exchange_value)
                        : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                        : "cc", "memory");
  return exchange_value;
}

3. Volatile

3.1 字節(jié)碼層面

  static volatile int greaterThanSevenCnt;
    descriptor: I
    flags: ACC_STATIC, ACC_VOLATILE

3.2 JVM層面

Github源碼

可以看到判斷是否是volatile字段,是的話(huà)最后會(huì)有OrderAccess::storeload(); , 就是就是storeload屏障

CASE(_putfield):
CASE(_putstatic):
    {
          // .... 省略若干行 
          // ....
          // Now store the result 現(xiàn)在要開(kāi)始存儲(chǔ)結(jié)果了
          // ConstantPoolCacheEntry* cache;     -- cache是常量池緩存實(shí)例
          // cache->is_volatile()               -- 判斷是否有volatile訪問(wèn)標(biāo)志修飾
          int field_offset = cache->f2_as_index();
          if (cache->is_volatile()) { // ****重點(diǎn)判斷邏輯**** 
            // volatile變量的賦值邏輯
            if (tos_type == itos) {
              obj->release_int_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == atos) {// 對(duì)象類(lèi)型賦值
              VERIFY_OOP(STACK_OBJECT(-1));
              obj->release_obj_field_put(field_offset, STACK_OBJECT(-1));
              OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)obj >> CardTableModRefBS::card_shift], 0);
            } else if (tos_type == btos) {// byte類(lèi)型賦值
              obj->release_byte_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == ltos) {// long類(lèi)型賦值
              obj->release_long_field_put(field_offset, STACK_LONG(-1));
            } else if (tos_type == ctos) {// char類(lèi)型賦值
              obj->release_char_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == stos) {// short類(lèi)型賦值
              obj->release_short_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == ftos) {// float類(lèi)型賦值
              obj->release_float_field_put(field_offset, STACK_FLOAT(-1));
            } else {// double類(lèi)型賦值
              obj->release_double_field_put(field_offset, STACK_DOUBLE(-1));
            }
            // *** 寫(xiě)完值后的storeload屏障 ***
            OrderAccess::storeload();
          } else {
            // 非volatile變量的賦值邏輯
            if (tos_type == itos) {
              obj->int_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == atos) {
              VERIFY_OOP(STACK_OBJECT(-1));
              obj->obj_field_put(field_offset, STACK_OBJECT(-1));
              OrderAccess::release_store(&BYTE_MAP_BASE[(uintptr_t)obj >> CardTableModRefBS::card_shift], 0);
            } else if (tos_type == btos) {
              obj->byte_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == ltos) {
              obj->long_field_put(field_offset, STACK_LONG(-1));
            } else if (tos_type == ctos) {
              obj->char_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == stos) {
              obj->short_field_put(field_offset, STACK_INT(-1));
            } else if (tos_type == ftos) {
              obj->float_field_put(field_offset, STACK_FLOAT(-1));
            } else {
              obj->double_field_put(field_offset, STACK_DOUBLE(-1));
            }
          }
          UPDATE_PC_AND_TOS_AND_CONTINUE(3, count);
  }

進(jìn)入OrderAccess源碼可以看到,直接執(zhí)行了一段匯編指令,并且有l(wèi)ock前綴

inline void OrderAccess::storeload()  { fence(); }
inline void OrderAccess::fence() {
  if (os::is_MP()) {
    // always use locked addl since mfence is sometimes expensive
#ifdef AMD64
    __asm__ volatile ("lock; addl $0,0(%%rsp)" : : : "cc", "memory");
#else
    __asm__ volatile ("lock; addl $0,0(%%esp)" : : : "cc", "memory");
#endif
  }
}

4. lock指令

在上面的分析中,最底層都設(shè)計(jì)到匯編層面的lock指令,這個(gè)指令有什么作用呢?

根據(jù)匯編參考文檔IA-32 Assembly Language Reference Manual

The LOCK # signal is asserted during execution of the instruction following the lock prefix. This signal can be used in a multiprocessor system to ensure exclusive use of shared memory while LOCK # is asserted. The bts instruction is the read-modify-write sequence used to implement test-and-run. The lock prefix works only with the instructions listed here. If a lock prefix is used with any other instructions, an undefined opcode trap is generated.

Lock是一個(gè)指令前綴,用于多核處理器系統(tǒng)不使用共享內(nèi)存

那么它又是怎么讓其他核心不訪問(wèn)共享內(nèi)存,有兩種方法

  • 鎖內(nèi)存總線(xiàn),也就是說(shuō)執(zhí)行這條指令的時(shí)候,其他的核心都不能在訪問(wèn)內(nèi)存了
  • 鎖緩存行,現(xiàn)在CPU本身是有多級(jí)緩存的,而這些緩存是如何保持一致的,由MESI來(lái)支持,MESI協(xié)議可以保證其他核心不使用內(nèi)存,或者換一種說(shuō)法,可以使用,但被修改的內(nèi)容會(huì)失效

5. MESI協(xié)議

現(xiàn)代CPU多核架構(gòu)中為了協(xié)調(diào)快速的CPU運(yùn)算和相對(duì)較慢的內(nèi)存讀寫(xiě)速度之間的矛盾,在CPU和內(nèi)存之間引入了CPU cache:

MESI協(xié)議下,緩存行(cache line)有四種狀態(tài)來(lái)保證緩存的一致性

  • 已修改Modified (M) 緩存行是臟的,與主存的值不同。如果別的CPU內(nèi)核要讀主存這塊數(shù)據(jù),該緩存行必須回寫(xiě)到主存,狀態(tài)變?yōu)楣蚕?S)
  • 獨(dú)占Exclusive (E) 緩存行只在當(dāng)前緩存中,但是干凈的(clean)–緩存數(shù)據(jù)同于主存數(shù)據(jù)。當(dāng)別的緩存讀取它時(shí),狀態(tài)變?yōu)楣蚕?;?dāng)前寫(xiě)數(shù)據(jù)時(shí),變?yōu)橐研薷臓顟B(tài)。
  • 共享Shared (S) 緩存行也存在于其它緩存中且是干凈的。緩存行可以在任意時(shí)刻拋棄。
  • 無(wú)效Invalid (I) 緩存行是無(wú)效的,需要從主內(nèi)存中讀取最新值

每次要修改緩存,如果緩存行狀態(tài)為 S 的話(huà)都要先發(fā)一個(gè) invalidate 的廣播,再等其他 CPU 將緩存行設(shè)置為無(wú)效后返回 invalidate ack 才能寫(xiě)到 Cache 中,因?yàn)檫@樣才能保證緩存的一致性

但是如果 CPU 頻繁地修改數(shù)據(jù),就會(huì)不斷地發(fā)送廣播消息,CPU 只能被動(dòng)同步地等待其他 CPU 的消息,顯然會(huì)對(duì)執(zhí)行效率產(chǎn)生影響

為了解決此問(wèn)題,工程師在 CPU 和 cache 之間又加了一個(gè) store buffer,同時(shí)在cache和總線(xiàn)之間添加了Invalidate Queue

這個(gè)buffer可以讓廣播和收廣播的處理異步化,效率當(dāng)然會(huì)變高,但強(qiáng)一致性變?yōu)榱俗罱K一致性

lock指令是CPU硬件工程師給程序員留的一個(gè)口子,把對(duì)MESI協(xié)議的優(yōu)化(store buffer, invalidate queue)禁用,暫時(shí)以同步方式工作,使得對(duì)于該關(guān)鍵字的MESI協(xié)議退回強(qiáng)一致性狀態(tài)

6. 總結(jié)

分析到此:

所有的并發(fā)問(wèn)題可以概括為,多個(gè)核心同時(shí)修改內(nèi)存數(shù)據(jù),導(dǎo)致結(jié)果不符合預(yù)期

解決并發(fā)問(wèn)題的方法可以概括為,同一時(shí)間只能讓一個(gè)核心修改內(nèi)存,但有多種手段,例如鎖總線(xiàn)、或者廣播讓其他核心失效

7. 其他問(wèn)題

既然sychronized的和volatile底層實(shí)現(xiàn)是一樣的,那么volatile為什么沒(méi)有原子性呢?

在于鎖定的范圍,volatile修飾的是一個(gè)字段,只能保證讀和寫(xiě)是原子性的,但讀出來(lái)、在計(jì)算、寫(xiě)入分為三步則不是原子性的。

sychronized底層也用了volatile的,但它的鎖定范圍是程序員指定的,這個(gè)范圍之間的代碼是原子的

cas volatile變量開(kāi)始鎖定
任意程序代碼
cas volatile變量釋放鎖定
  • 現(xiàn)在一般推薦使用Java的Atomic類(lèi),他是通過(guò)CAS來(lái)實(shí)現(xiàn)的,它和sychronized的區(qū)別是什么?

    cas不能單獨(dú)使用,需要加自旋操作,本身是一個(gè)樂(lè)觀鎖

    sychronized本身結(jié)合了樂(lè)觀鎖和悲觀鎖,悲觀鎖會(huì)讓線(xiàn)程park然后重試,不會(huì)消耗CPU,而樂(lè)觀鎖但不斷消耗cpu

8. 對(duì)比

在閱讀ObjectMonitor代碼時(shí),發(fā)現(xiàn)有很熟悉的感覺(jué)

發(fā)現(xiàn)這些鎖的數(shù)據(jù)結(jié)果都是類(lèi)似的,一個(gè)volatile變量加一個(gè)等待隊(duì)列

參考

【1】]synchronized 關(guān)鍵字底層原理

【2】Java多線(xiàn)程:objectMonitor源碼解讀(3)

【3】Linux Kernel CMPXCHG函數(shù)分析

【4】聊聊CPU的LOCK指令

【5】12 張圖看懂 CPU 緩存一致性與 MESI 協(xié)議,真的一致嗎?

【6】MESI和volatile的關(guān)系詳解

【7】volatile底層原理詳解

【8】淺析mutex實(shí)現(xiàn)原理

【9】CAS你以為你真的懂?

【10】x86 LOCK 指令前綴

【11】Linux Mutex機(jī)制分析

到此這篇關(guān)于Java的volatile和sychronized底層實(shí)現(xiàn)原理解析的文章就介紹到這了,更多相關(guān)Java volatile和sychronized底層內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mybatis in查詢(xún)條件過(guò)長(zhǎng)的解決方案

    mybatis in查詢(xún)條件過(guò)長(zhǎng)的解決方案

    這篇文章主要介紹了mybatis in查詢(xún)條件過(guò)長(zhǎng)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java經(jīng)典設(shè)計(jì)模式之觀察者模式原理與用法詳解

    Java經(jīng)典設(shè)計(jì)模式之觀察者模式原理與用法詳解

    這篇文章主要介紹了Java經(jīng)典設(shè)計(jì)模式之觀察者模式,簡(jiǎn)單分析了觀察者模式的概念、原理并結(jié)合實(shí)例形式給出了java觀察者模式的具體用法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-08-08
  • Java畢業(yè)設(shè)計(jì)之多用戶(hù)宿舍管理系統(tǒng)的實(shí)現(xiàn)

    Java畢業(yè)設(shè)計(jì)之多用戶(hù)宿舍管理系統(tǒng)的實(shí)現(xiàn)

    這篇文章主要介紹了基于Java實(shí)現(xiàn)的多用戶(hù)宿舍管理系統(tǒng),本文采用了jsp、servlet、jdbc等技術(shù),文中示例代碼講解詳細(xì),需要的可以參考一下
    2022-02-02
  • Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之紅黑樹(shù)

    Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之紅黑樹(shù)

    紅黑樹(shù)的應(yīng)用比較廣泛,主要是用它來(lái)存儲(chǔ)有序的數(shù)據(jù),它的時(shí)間復(fù)雜度是O(lgn),效率非常之高。例如,Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虛擬內(nèi)存的管理,都是通過(guò)紅黑樹(shù)去實(shí)現(xiàn)的
    2022-02-02
  • java 如何往已經(jīng)存在的excel表格里面追加數(shù)據(jù)的方法

    java 如何往已經(jīng)存在的excel表格里面追加數(shù)據(jù)的方法

    這篇文章主要介紹了java 如何往已經(jīng)存在的excel表格里面追加數(shù)據(jù)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Spring Boot 自定義 Shiro 過(guò)濾器無(wú)法使用 @Autowired問(wèn)題及解決方法

    Spring Boot 自定義 Shiro 過(guò)濾器無(wú)法使用 @Autowired問(wèn)題及解決方法

    這篇文章主要介紹了Spring Boot 自定義 Shiro 過(guò)濾器無(wú)法使用 @Autowired問(wèn)題及解決方法 ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-06-06
  • 詳解SpringBoot如何自定義自己的Starter組件

    詳解SpringBoot如何自定義自己的Starter組件

    這篇文章主要為大家詳細(xì)介紹了在SpringBoot中如何自定義自己的Starter組件,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • BigDecimal的加減乘除計(jì)算方法詳解

    BigDecimal的加減乘除計(jì)算方法詳解

    小編做題遇到了大數(shù)的精確計(jì)算,再次認(rèn)識(shí)了bigdecimal關(guān)于Bigdecimal意外的有許多小知識(shí)點(diǎn)和坑,這里特此整理一下為方便以后學(xué)習(xí),希望能幫助到其他的萌新
    2021-08-08
  • Spring框架之IOC介紹講解

    Spring框架之IOC介紹講解

    IOC-Inversion of Control,即控制反轉(zhuǎn)。它不是什么技術(shù),而是一種設(shè)計(jì)思想。這篇文章將為大家介紹一下Spring控制反轉(zhuǎn)IOC的原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • Springboot引入多個(gè)yml方法(多種方案)

    Springboot引入多個(gè)yml方法(多種方案)

    SpringBoot默認(rèn)加載的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中激活該文件這篇文章主要介紹了Springboot引入多個(gè)yml方法,需要的朋友可以參考下
    2019-10-10

最新評(píng)論

镇巴县| 昌邑市| 潮安县| 忻州市| 隆林| 临泽县| 金堂县| 杭锦旗| 兴义市| 怀远县| 凯里市| 武安市| 乌兰察布市| 汤原县| 宁强县| 达拉特旗| 葵青区| 突泉县| 故城县| 永康市| 太和县| 南宫市| 当雄县| 攀枝花市| 绥宁县| 东海县| 饶阳县| 德钦县| 石城县| 元江| 呼图壁县| 海南省| 阳谷县| 仲巴县| 安陆市| 南华县| 和龙市| 浦北县| 股票| 来凤县| 大足县|