亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java并發編程之介紹線程安全基礎的示例

發布時間:2021-02-24 10:59:09 來源:億速云 閱讀:157 作者:小新 欄目:編程語言

這篇文章主要介紹了Java并發編程之介紹線程安全基礎的示例,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。


線程安全基礎

  • 1.線程安全問題

  • 2.賬戶取款案例

  • 3.同步代碼塊synchronized


    • synchronized的理解

    • java中有三大變量的線程安全問題

    • 在實例方法上使用synchronized

    • 總結

    • 面試題

  • 4.死鎖

  • 5.開發中應該怎么解決線程安全問題

  • 6.守護線程

  • 7.定時器

  • 8.實現線程的第三種方式:實現Callable接口

  • 9.Object類中的wait和notify方法

  • 10.生產者和消費者

1.線程安全問題

2.1、為什么這個是重點?
??以后在開發中,我們的項目都是運行在服務器當中,而服務器已經將線程的定義,線程對象的創建,線程的啟動等,都已經實現完了。這些代碼我們都不需要編寫。
??最重要的是:你要知道,你編寫的程序需要放到一個多線程的環境下運行,你更需要關注的是這些數據在多線程并發的環境下是否是安全的。(重點:*****)
2.2、什么時候數據在多線程并發的環境下會存在安全問題呢?
Java并發編程之介紹線程安全基礎的示例
??三個條件:
??條件1:多線程并發。
??條件2:有共享數據。
??條件3:共享數據有修改的行為。

??滿足以上3個條件之后,就會存在線程安全問題。
2.3、怎么解決線程安全問題呢?
當多線程并發的環境下,有共享數據,并且這個數據還會被修改,此時就存在線程安全問題,怎么解決這個問題?
??線程排隊執行。(不能并發)。
??用排隊執行解決線程安全問題。
??這種機制被稱為:線程同步機制。

??專業術語叫做:線程同步,實際上就是線程不能并發了,線程必須排隊執行
??怎么解決線程安全問題呀?
??使用“線程同步機制”。
??線程同步就是線程排隊了,線程排隊了就會犧牲一部分效率,沒辦法,數據安全第一位,只有數據安全了,我們才可以談效率。數據不安全,沒有效率的事兒。
2.4、說到線程同步這塊,涉及到這兩個專業術語:
異步編程模型:
??線程t1和線程t2,各自執行各自的,t1不管t2,t2不管t1,
??誰也不需要等誰,這種編程模型叫做:異步編程模型。
??其實就是:多線程并發(效率較高。)

同步編程模型:
??線程t1和線程t2,在線程t1執行的時候,必須等待t2線程執行結束,或者說在t2線程執行的時候,必須等待t1線程執行結束,兩個線程之間發生了等待關系,這就是同步編程模型。效率較低。線程排隊執行。

異步就是并發。同步就是排隊。

2.賬戶取款案例

Account類

package ThreadSafe;public class Account {
	//賬號
	private String actno;
	//余額
	private double balance;
	
	public Account(String actno, double balance) {
		super();
		this.actno = actno;
		this.balance = balance;
	}
	public String getActno() {
		return actno;
	}
	public void setActno(String actno) {
		this.actno = actno;
	}
	public double getBalance() {
		return balance;
	}
	public void setBalance(double balance) {
		this.balance = balance;
	}
	//取款的方法
	public void withdraw(double money){
		//t1和t2并發執行這個方法(t1和t2是兩個棧 ,兩個棧操作堆中同一個對象)
		//取款之前的余額
		double before=this.getBalance();
		//取款之后的余額
		double after=before-money;
		//模擬一下網絡延遲,會出現問題
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//更新余額
		//思考:t1執行到這里了,但還沒有來得及執行這行代碼,t2線程進來withdraw方法了,此時一定出問題
		this.setBalance(after);
	}}AccountThread類public class AccountThread extends Thread{
	//兩個線程必須共享一個賬戶對象
	private Account act;
	//通過構造方法傳遞過來賬戶對象
	public AccountThread(Account act) {
		this.act = act;
	}
	@Override
	public void run() {
		//假設取款5000
		double money=5000;
		//多線程執行這個方法
		act.withdraw(money);
		System.out.println(Thread.currentThread().getName()+"賬戶"+act.getActno()+"取款成功"+act.getBalance());
	}}

Test類

public class Test {
	public static void main(String[] args) {
		//創建賬戶對象
		Account act=new Account("act-001",10000);
		//創建兩個線程
		Thread t1=new AccountThread(act);
		Thread t2=new AccountThread(act);
		
		t1.setName("t1");
		t2.setName("t2");
		//啟動兩個線程執行
		t1.start();
		t2.start();
	}}

Java并發編程之介紹線程安全基礎的示例

3.同步代碼塊synchronized

synchronized的理解

//以下這幾行代碼必須是線程排隊的,不能并發
//一個線程把這里的代碼全部執行結束后,另一個線程才能進來
/*
線程同步機制的語法是
synchronized(){
//線程同步代碼塊。
}

synchronized后面小括號中傳的這個數據是相當關鍵的,
這個數據必須是多線程共享的數據,才能達到多線程排隊。
()中寫什么?
那要看你想讓哪些線程同步
假設t1、t2、t3、t4、t5有5個線程
你只希望t1 t2 t3排隊,t4 t5不需要排隊,怎么辦
你一定要在()中寫一個t1 t2 t3共享的對象,而這個對象對于t4 t5來說不是共享的
這里的共享對象是賬戶對象
賬戶對象是共享的,this是賬戶對象
有時不一定是this,這里只要是多線程共享的那個對象就行

		synchronized(this){
			double before=this.getBalance();
			double after=before-money;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			this.setBalance(after);
		}

Java并發編程之介紹線程安全基礎的示例在java語言中,任何對象都有一把鎖,其實這把鎖就是一個標記,(只是把它叫做鎖)
100個對象,100個鎖,1個對象1把鎖。
以上代碼的執行原理是什么呢?
1.假設t1和t2線程并發,開始執行以上代碼的時候,肯定有一個先一個后,
2.假設t1先執行了,遇到了synchronized,這個時候自動找后面共享對象的對象鎖,找到之后,并占有這把鎖,然后執行同步代碼塊中的程序,在程序執行過程中一直都占有這把鎖,直到同步代碼塊執行結束,這把鎖才會釋放。
3.假設t1已經占有這把鎖,此時t2也遇到synchronized關鍵字,也會去占有后面共享對象的這把鎖,結果這把鎖被t1占有,t2只能在同步代碼塊外邊等待t1的結束,直到t1把同步代碼塊執行結束了,t1會歸還這把鎖,此時t2終于等到這把鎖,然后t2占有這把鎖之后,進入同步代碼塊執行程序。
這樣就達到了線程排隊執行
這里需要注意的是:這個共享對象一定要選好了,這個共享對象一定是你需要排隊執行的這些線程對象所共享的。
//對象
Object obj=new Object(); //實例變量(Account對象是多線程共享的,Account對象中的實例變量obj也是共享的)
synchronized(obj){}

Java并發編程之介紹線程安全基礎的示例
括號里邊只要是共享對象就行。
Object obj2=new Object(); //局部變量
synchronized(obj2){}
這樣寫就不安全了,因為obj2是局部變量,不是共享對象。
synchronized(“abc”){}
這樣寫時可以的。存在字符串常量池中
寫"abc"的話所有線程都會同步

而如果是寫synchronized(this){}的話,我們創建了一個新的對象act2可不用共享對象。
所以最好是寫synchronized(this){},比如你要取款,要讓其他取別的賬戶的人也要等嗎?不應該,只有同時對你這1個賬戶取款的時候,需要等待,別人取錢的時候,需要從其他賬戶中取錢,就不需要等待。

java中有三大變量的線程安全問題

實例變量,在堆中
靜態變量,在方法區
局部變量,在棧中

以上三大變量
局部變量永遠不會存在線程安全問題
因為局部變量不共享(一個線程一個棧)
局部變量在棧中,所以局部變量永遠都不會共享
實例變量在堆中,堆只有1個。
靜態變量在方法區中,方法區只有1個

堆和方法區都是多線程共享的,所以可能存在線程安全問題

局部變量+常量:不會有線程安全問題。
成員變量:可能會有線程安全問題

同步代碼塊越小,效率越高。

//多線程執行這個方法//synchronized(this)//這里的this是AccountThread對象,這個對象不共享。synchronized(act){
			act.withdraw(money);
			System.out.println(Thread.currentThread().getName()+"賬戶"+act.getActno()+"取款成功,余額:"+act.getBalance());}

在實例方法上使用synchronized

在實例方法上可以使用synchronized嗎?可以的。
synchronized出現在實例方法上,一定鎖的是this
沒得挑,只能是this,不能是其他的對象了
所以這種方式不靈活。
另外還有一個缺點:synchronized出現在實例方法上,表示整個方法體都需要同步
可能會擴大同步的范圍,導致程序的執行效率降低。所以這種方式不常用
synchronized使用在實例方法上有什么優點?
代碼寫的少了,節儉了
如果共享的對象是this,并且需要同步的代碼是整個方法體,建議使用這種方式
StringBuffer就是在每個方法上加了synchronized關鍵字

使用局部變量的話,最好使用StringBuilder
因為局部變量不存在線程安全問題,選擇StringBuilder,StringBuffer效率比較低。
ArrayList是非線程安全的。
Vector是線程安全的。
HashMap HashSet是非線程安全的。
Hashtable是線程安全的

總結

synchronized有三種寫法:

??第一種:同步代碼塊
????靈活
??????synchronized(線程共享對象){
????????同步代碼塊;
??????}
??第二種:在實例方法上使用synchronized
??????表示共享對象一定是this
??????并且同步代碼塊是整個方法體。
??第三種:在靜態方法上使用synchronized
??????表示找類鎖。
??????類鎖永遠只有1把。
??????就算創建了100個對象,那類鎖也只有一把。

對象鎖:1個對象1把鎖,100個對象100把鎖。
類鎖:100個對象,也可能只是1把類鎖。

面試題

//面試題:doother方法的執行需不需要等待dosome方法的結束。
//不需要,因為doother方法沒有synchronized

public class exam01 {
	public static void main(String[] args) {
		MyClass mc=new MyClass();
		Thread t1=new MyThread(mc);
		Thread t2=new MyThread(mc);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		try {
			Thread.sleep(1000);		//這個睡眠的作用是:為了保證t1線程先執行
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t2.start();
	}}class MyThread extends Thread{
	private MyClass mc;
	public MyThread(MyClass mc) {
		super();
		this.mc = mc;
	}
	public void run(){
		if(Thread.currentThread().getName().equals("t1")){
			mc.dosome();
		}
		if(Thread.currentThread().getName().equals("t2")){
			mc.doOther();
		}
	}
	}class MyClass{
	public synchronized void dosome(){
		System.out.println("doSome begin");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("doSome end");
	}
	public void doOther(){
		System.out.println("doOther begin");
		System.out.println("doOther end");
	}}

Java并發編程之介紹線程安全基礎的示例 當在doother上面加了synchronized呢
//面試題:doother方法的執行需不需要等待dosome方法的結束。
//需要,因為doother方法沒有synchronized

	public synchronized void doOther(){
		System.out.println("doOther begin");
		System.out.println("doOther end");
	}

//面試題:doother方法的執行需不需要等待dosome方法的結束。
//不用排隊,誰也不用管誰

MyClass mc1=new MyClass();MyClass mc2=new MyClass();Thread t1=new MyThread(mc1);Thread t2=new MyThread(mc2);

//面試題:doother方法的執行需不需要等待dosome方法的結束。
//需要,因為靜態方法是類鎖,類鎖不管創建了幾個對象,類鎖只有一把

MyClass mc1=new MyClass();MyClass mc2=new MyClass();Thread t1=new MyThread(mc1);Thread t2=new MyThread(mc2);public synchronized static void dosome(){
		System.out.println("doSome begin");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("doSome end");
	}
	public synchronized static void doOther(){
		System.out.println("doOther begin");
		System.out.println("doOther end");
	}

這種鎖叫排他鎖:

4.死鎖

Java并發編程之介紹線程安全基礎的示例
synchronized在開發中最好不要嵌套使用,一不小心就可能導致死鎖。
死鎖代碼要會寫。
一般面試官要求你會寫
只有會寫的,才會在以后的開發中注意這個事兒
因為死鎖很難調試。

public class DeadLock {
	public static void main(String[] args) {
		Object o1=new Object();
		Object o2=new Object();
		//t1線程和t2線程共享o1,o2
		Thread t1=new MyThread1(o1,o2);
		Thread t2=new MyThread2(o1,o2);
		t1.start();
		t2.start();
	}}class MyThread1 extends Thread{
	Object o1;
	Object o2;
	public MyThread1(Object o1,Object o2){
		this.o1=o1;
		this.o2=o2;
	}
	@Override
	public void run() {
		synchronized (o1) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (o2) {
				
			}
		}
	}}class MyThread2 extends Thread{
	Object o1;
	Object o2;
	public MyThread2(Object o1,Object o2){
		this.o1=o1;
		this.o2=o2;
	}
	@Override
	public void run() {
		synchronized (o2) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (o1) {
				
			}
		}
	}}

5.開發中應該怎么解決線程安全問題

聊一聊,我們以后開發中應該怎么解決線程安全問題?
??是一上來就選擇線程同步嗎?synchronized
??不是,synchronized會讓程序的執行效率降低,用戶體驗不好。
??系統的用戶吞吐量降低。用戶體驗差。在不得已的情況下再選擇
??線程同步機制。
??第一種方案:盡量使用局部變量代替“實例變量和靜態變量”。
??第二種方案:如果必須是實例變量,那么可以考慮創建多個對象,這樣實例變量的內存就不共享了。(一個線程對應1個對象,100個線程對應100個對象,對象不共享,就沒有數據安全問題了。)
??第三種方案:如果不能使用局部變量,對象也不能創建多個,這個時候就只能選擇synchronized了。線程同步機制
線程這塊還有那些內容呢?列舉一下
7.1、守護線程
7.2、定時器
7.3、實現線程的第三種方式:FutureTask方式,實現Callable接口。(JDK8新特性。)
7.4、關于Object類中的wait和notify方法。(生產者和消費者模式!)

6.守護線程

守護線程
java語言中線程分為兩大類:
??一類是:用戶線程
??一類是:守護線程(后臺線程)

其中具有代表性的就是:垃圾回收線程(守護線程)。
??守護線程的特點:
??一般守護線程是一個死循環,所有的用戶線程只要結束,
??守護線程自動結束。
??注意:主線程main方法是一個用戶線程。
??守護線程用在什么地方呢?
??每天00:00的時候系統數據自動備份。
??這個需要使用到定時器,并且我們可以將定時器設置為守護線程。
??一直在那里看著,沒到00:00的時候就備份一次。所有的用戶線程如果結束了,守護線程自動退出,沒有必要進行數據備份了。

package testThread;/*
實現守護線程
 * */
	public class ThreadTest13 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread t=new BakDataThread();
		t.setName("備份數據的線程");
		//啟動之前,將線程設置為守護線程
		t.setDaemon(true);
		t.start();
		//主線程:主線程是用戶線程
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+"---->"+i);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}}class BakDataThread extends Thread{
	@Override
	public void run() {
		int i=0;
		//即使是死循環,但由于該線程是守護者,當用戶線程結束,守護線程自動終止
		while(true){
			System.out.println(Thread.currentThread().getName()+"---->"+(++i));
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}}

7.定時器

定時器的作用:
??間隔特定的時間,執行特定的程序。
??每周要進行銀行賬戶的總賬操作。
??每天要進行數據的備份操作。
??在實際的開發中,每隔多久執行一段特定的程序,這種需求是很常見的,
那么在java中其實可以采用多種方式實現:
??可以使用sleep方法,睡眠,設置睡眠時間,沒到這個時間點醒來,執行任務。這種方式是最原始的定時器。(比較low)
??在java的類庫中已經寫好了一個定時器:java.util.Timer,可以直接拿來用。不過,這種方式在目前的開發中也很少用,因為現在有很多高級框架都是支持定時任務的。

??在實際的開發中,目前使用較多的是Spring框架中提供的SpringTask框架,這個框架只要進行簡單的配置,就可以完成定時器的任務。

8.實現線程的第三種方式:實現Callable接口

實現線程的第三種方式:實現Callable接口。(JDK8新特性。)
這種方式實現的線程可以獲取線程的返回值
之前講解的那兩種方式是無法獲取線程返回值的,因為run方法返回void。
思考:
系統委派一個線程去執行一個任務,該線程執行完任務之后,可能會有一個執行結果,我們怎么能拿到這個執行結果呢
使用第三種方式:實現Callable接口方式

public class ThreadTest14 {
	public static void main(String[] args) throws Exception, ExecutionException {
		//第一步:創建一個未來任務類對象
		//參數非常重要,需要給一個callable接口的實現類對象
		FutureTask task=new FutureTask(new Callable(){
			@Override
			//call方法相當于是run方法,只不過這個有返回值,線程執行一個任務,執行之后可能會有一個執行結果。
			public Object call() throws Exception {
				System.out.println("call method begin");
				Thread.sleep(1000);
				System.out.println("call method begin");
				int a=100;
				int b=200;
				return a+b;			//自動裝箱
			}		
		});
		//創建線程對象
		Thread t=new Thread(task);
		
		//啟動線程
		t.start();
		
		//這里是main方法,這是在主線程中
		//在線程中,怎么獲取t線程的執行結果
		//get方法的執行會導致當前線程阻塞
		Object obj=task.get();
		System.out.println("線程執行結果"+obj);
		//main方法這里的程序要想執行必須等待get()方法的結束
		//而get方法可能需要很久。因為get()方法是為了拿另一個線程的執行結果。
		//另一個線程的執行是需要時間的
		System.out.println("hello,world");
	}}

這種方式的優點:可以獲取到線程的執行結果
這種方式的缺點:效率比較低,在獲取t線程執行結果的時候,當前線程受阻塞,效率較低。

9.Object類中的wait和notify方法

(生產者和消費者模式!)
??第一:wait和notify方法不是線程對象的方法,是java中任何一個java對象都有的方法,因為這兩個方式是Object類中自帶的。
??wait方法和notify方法不是通過線程對象調用,

??不是這樣的:t.wait(),也不是這樣的:t.notify()…不對。
??第二:wait()方法作用?
????Object o = new Object();
????o.wait();
??表示:
??讓正在o對象上活動的線程進入等待狀態,無期限等待,直到被喚醒為止。
??o.wait();方法的調用,會讓“當前線程(正在o對象上活動的線程)”進入等待狀態
??第三:notify()方法作用?
????Object o = new Object();
????o.notify();
??表示:
??喚醒正在o對象上等待的線程
??還有一個notifyAll()方法:
??這個方法是喚醒o對象上處于等待的所有線程。

Java并發編程之介紹線程安全基礎的示例

10.生產者和消費者

Java并發編程之介紹線程安全基礎的示例
1.使用wait方法和notify方法實現生產者和消費者模式
2.什么是生產者和消費者模式?
生產線程負責生產,消費線程負責消費
生產線程和消費線程要達到均衡
這是一種特殊的業務需求,在這種特殊的情況下需要使用wait方法和notify方法
3.wait和notify方法不是線程對象的方法,是普通java對象都有的方法
4.wait方法和notify方法是建立在線程同步的基礎之上。因為多線程要同時操作一個倉庫,有線程安全問題
5.wait方法作用:o.wait()讓正在o對象上活動的線程t進入等待狀態,并且釋放掉t線程之前占有的o對象的鎖
6.notify方法的作用:o.notify()讓正在o對象上等待的線程喚醒,只是5通知,不會釋放o對象上之前占有的鎖
7.模擬這樣一個需求:
倉庫我們采用list集合
list集合中假設只能存儲1個元素
1個元素就表示倉庫滿了
如果list集合中的元素個數是0,就表示倉庫空了。
保證list集合中永遠都是最多存儲1個元素
必須做到這種效果,生產1個消費1個。

public class ThreadTest15 {
	public static void main(String[] args) {
		//創建一個倉庫獨享,共享的
		List list=new ArrayList();
		//創建兩個線程對象
		//生產者線程
		Thread t1=new Thread(new Producer(list));
		//消費者線程
		Thread t2=new Thread(new Consumer(list));
		t1.setName("生產者線程");
		t2.setName("消費者線程");
		t1.start();
		t2.start();
	}}//生產線程class Producer implements Runnable{
	//倉庫
	private List list;
	public Producer(List list) {
		this.list = list;
	}
	public void run() {
		//一直生產
		while(true){
			//給倉庫對象list加鎖
			synchronized (list) {
				if(list.size()>0){	//大于0說明倉庫中已經有1個元素了
					//當前線程進入等待狀態,并且釋放list集合的鎖
					try {
						list.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				//程序能夠執行到這里說明倉庫是空的,可以生產
				Object obj =new Object();
				list.add(obj);
				System.out.println(Thread.currentThread().getName()+"---->"+obj);
				//喚醒消費者進行消費
				list.notifyAll();
			}
			
		}
	}}//消費線程class Consumer implements Runnable{
	//倉庫
	private List list;
	public Consumer(List list) {
		this.list = list;
	}
	public void run() {
		//一直消費
		while(true){
			//給倉庫對象list加鎖
			synchronized (list) {
				if(list.size()==0){	//倉里已經空了
					try {
						//倉庫已經空了
						//消費者線程等待,并釋放掉list集合鎖
						list.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			Object obj=list.remove(0);
			System.out.println(Thread.currentThread().getName()+"---->"+obj);
			//喚醒生產者生產
			list.notifyAll();
		}
	}}

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Java并發編程之介紹線程安全基礎的示例”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

湖州市| 高淳县| 新河县| 铜梁县| 崇仁县| 柏乡县| 武宣县| 监利县| 亚东县| 固阳县| 永城市| 中西区| 闸北区| 江永县| 浪卡子县| 康平县| 杂多县| 金湖县| 郴州市| 淅川县| 莆田市| 蒙城县| 伊川县| 苏州市| 北宁市| 陕西省| 濮阳县| 海门市| 昂仁县| 团风县| 金山区| 西安市| 绥滨县| 手游| 康定县| 浦东新区| 尼勒克县| 平远县| 中西区| 镇江市| 南城县|