国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java多線程編程核心技術(shù) 第二章

2019-11-06 08:13:26
字體:
供稿:網(wǎng)友

java多線程編程核心技術(shù) 第二章

對象及變量的并發(fā)訪問

synchronized同步方法

“非線程安全”有可能導(dǎo)致“臟讀”,即取到的數(shù)據(jù)被更改過的。

當(dāng)多個線程共同訪問1個對象的實例變量,可能會不安全。

“線程安全”——獲得的實例變量的值經(jīng)過同步處理

把變量放在方法里面,這時變量是私有的,就是安全的。synchronized()生成的是對象鎖

*為什么實例化不同的對象,線程就會異步操作?

因為當(dāng)多個線程訪問多個對象時,JVM會創(chuàng)建多個鎖,比如,A線程要調(diào)用對象X的鎖的方法,B線程要調(diào)用對象M的鎖的方法,B不用等A執(zhí)行完,就可以調(diào)用M的鎖。

*只有共享資源的讀寫訪問才需要同步化

如果沒有多個線程對同一個對象的實例變量操作,就不會出現(xiàn)線程不安全問題,不需要用同步。

*兩個線程訪問一個同步方法,一個非同步方法,訪問非同步的線程可以用異步方式調(diào)用非synchronized類型的方法

臟讀

讀取實例變量時,此值已經(jīng)被其他線程更改過了。

用synchronized解決

A線程調(diào)用synchronized X方法時,B線程不能調(diào)用X方法,但是B可以其他非synchronized的方法。如果B線程也要調(diào)用其他synchronized的非X方法,要等A線程調(diào)用完X方法,B才能調(diào)用其他synchronized的非X方法。

鎖重入

在鎖內(nèi)部調(diào)用本類的其他synchronized方法,永遠可以得到鎖。

出現(xiàn)異常,鎖自動釋放

同步不具有繼承性:父類的方法加了synchronized,不代表子類不用加synchronized就能上鎖?!宇惖姆椒ㄒ残枰觭ynchronized才有效

synchronized同步語句塊

弊端:當(dāng)A線程長時間調(diào)用synchronized的方法,B線程就需要等待那么長的時間解決:使用synchronized同步語句塊——半同步,半異步同步性:一個線程訪問synchronized(this)時,其他線程對同一個類的其他synchronized(this)代碼塊的訪問被阻塞同步語句塊是鎖定當(dāng)前對象的synchronized(非this對象) 鎖定非this對象的優(yōu)點:一個類中如果有很多synchronized方法,synchronized(非this)方法和synchronized(this)方法是異步的,提高了運行效率

例如:

public class Service { PRivate String usernameParam; private String passWordParam; public void setUsernamePassword (String username,String password) { try { String anyString = new String(); synchronized (anyString) { System.out.println ("線程名稱為:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"進入同步塊"); usernameParam = username; Thread.sleep(3000); passwordParam = password; System.out.println("線程名稱為:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"離開同步塊"); } } catch (InterruptedException e) { e.printStackTrace(); } }}

運行結(jié)果

線程名稱為:A在1403594007194進入同步塊 線程名稱為:B在1403594007194進入同步塊 線程名稱為:B在1403594010194離開同步塊 線程名稱為:A在1403594010194離開同步塊

從結(jié)果可看出是異步的,因為不是同一個鎖,這時候很容易出現(xiàn)“臟讀”

3個結(jié)論

當(dāng)多個線程同時執(zhí)行synchronized(x){ }同步代碼塊是呈同步效果。當(dāng)其他線程執(zhí)行x對象中synchronized同步方法時成同步效果。當(dāng)其他線程執(zhí)行x對象方法里面的synchronized(this)代碼塊時也是同步調(diào)用。

靜態(tài)同步synchronized方法與synchronized(class)代碼塊

synchronized用在static靜態(tài)方法上就是對當(dāng)前的*.java文件對應(yīng)的Class類進行持鎖(整個類鎖上了)。

與加到非static方法的使用效果是一樣的 本質(zhì)上的不同是synchronized加到static方法是給Class類上鎖,synchronized加到非靜態(tài)方法上是給對象上鎖

例如:

public class Service { synchronized public static void printA(){ try{ System.out.println("進入printA"); Thread.sleep(3000); System.out.println("離開printA"); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public static void printB(){ System.out.println("進入printB"); System.out.println("離開printB"); } synchronized public void printC() { System.out.println("進入printC"); System.out.println("離開printC"); }}

上面的printA方法和printB方法都是對Service類上鎖的,而printC方法是對對象上鎖的,所以同樣運行時,printC是異步的,printA和printB是同步的。 Class鎖對類的所有對象實例起作用,即不同對象但是靜態(tài)的同步方法仍是同步運行 同步synchronized(class)代碼塊的作用和synchronized static 方法的作用一樣,看下面例子如何上鎖——

public class Service { public static void printA(){ synchronized (Service.class) { try{ System.out.println("進入printA"); Thread.sleep(3000); System.out.println("離開printA"); } catch (InterruptedException e) { e.printStackTrace(); } } }}

數(shù)據(jù)類型String的常量池特性

JVM中具有String常量池緩存的功能將synchronized(string)同步塊與String聯(lián)合使用時,要注意常量帶來的一些例外。(P103有例子)大多數(shù)情況下,synchronized代碼塊不用String作為鎖對象,而改用其他。比如new Object()實例化一個對象,但它不放入緩存public class Service { public static void print(Object object) { try{ synchronized (object){ while (true){ System.out.println(Thread.currentThread().getName()); Thread.sleep(1000); } } } catch (InterruptedException e) { e.printStackTrace(); } }}public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { service.print(new Object()); }}public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { service.print(new Object()); }}public class Run { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(Service); a.setName("A"); a.start(); ThreadB b = new ThreadB(Service); b.setName("B"); b.start(); }}

運行結(jié)果:

A B B A A B B A A B B A 交替打印,持有的鎖不是一個。

同步synchronized方法無限等待解決

同步方法容易造成死循環(huán) 可以通過同步塊來解決 例如:synchronized public void methodA() { System.out.println("methodA begin"); boolean isContinueRun = true; while (isContinueRun) { } System.out.println("methodA end");}synchronized public void methodB() { System.out.println("methodB begin"); System.out.println("methodB end");}

上面代碼會造成死循環(huán)使其他同步方法無法運行,修改成用同步塊

Object object1 = new Object();public void methodA() { synchronized (object1) { System.out.println("methodA begin"); boolean isContinueRun = true; while (isContinueRun) { } System.out.println("methodA end"); }}Object object2 = new Object();public void methodB() { synchronized (object2) { System.out.println("methodB begin"); System.out.println("methodB end"); }}

死鎖

“死鎖”必須避免,不同線程在等待不可能被釋放的鎖會導(dǎo)致所有任務(wù)無法完成,造成線程的“假死”。

用JDK的工具監(jiān)測是否有死鎖現(xiàn)象 1. 進入CMD工具 2. 進入JDK安裝文件夾的bin目錄 3. 執(zhí)行jps命令 4. 執(zhí)行jstack命令

這里寫圖片描述

內(nèi)置類與靜態(tài)內(nèi)置類

什么是內(nèi)置類 看例子:

public class PublicClass { private String username; class PrivateClass { private String age; public String getAge(){ return age; } public void setAge(String age){ this.age = age; } } public String getUsername(){ return username; } public void setUsername(String username){ this.username = username; }}public class Run { public static void main(String[] args) { PublicClass publicClass = new PublicClass(); publicClass.setUsername("a"); System.out.println(publicClass.getUsername()); PrivateClass privateClass = publicClass.new PrivateClass(); //是這樣實例化的,如果PublicClass.java和Run.java不在同一個包中,則需要將PrivateClass內(nèi)置聲明成public privateClass.setAge("18"); System.out.println(privateClass.getAge()); }}

內(nèi)置類中還有一種叫靜態(tài)內(nèi)置類,看以下例子:

public class PublicClass { static private String username; static class PrivateClass { static private String age; public String getAge(){ return age; } public void setAge(String age){ this.age = age; } } public String getUsername(){ return username; } public void setUsername(String username){ this.username = username; }}public class Run { public static void main(String[] args) { PublicClass publicClass = new PublicClass(); publicClass.setUsername("a"); System.out.println(publicClass.getUsername()); PrivateClass privateClass = new PrivateClass(); //實例化 privateClass.setAge("18"); System.out.println(privateClass.getAge()); }}

內(nèi)置類與同步

同步代碼塊synchronized(class2)對class2上鎖,其他線程只能用同步的方式調(diào)用class2的靜態(tài)同步方式。public class OutClass{ static class InnerClass1{ public void method1(InnerClass2 class2){ synchronized (class2){ } } } static class InnerClass2 { public synchronized void method2(){ } }}public static void main(String[] args){ final InnerClass1 in1 = new InnerClass1(); final InnerClass2 in2 = new InnerClass2(); Thread t1 = new Thread (new Runnable(){ public void run(){ in1.method1(in2); } },"T1"); Thread2 t2 = new Thread(new Runnable(){ public void run(){ in2.method2(); } },"T2"); t1.start(); t2.start(); }

結(jié)果,要在method1()運行完之后method2()才能運行。

鎖對象的改變

將任何數(shù)據(jù)類型作為同步鎖時,注意是否有多個線程同時持有鎖對象,如果持有相同的鎖對象,則線程之間就是同步的;如果分別獲得鎖對象,線程之間是異步的。只要對象不變,即使對象的屬性被改變,運行結(jié)果還是同步。(就是如果對象變了,運行結(jié)果就會是異步)

volatile關(guān)鍵字

作用:(使變量在多個線程間可見)強制從公共堆棧中取得變量的值,而不是從線程私有數(shù)據(jù)棧中取得變量的值。

如果有“多繼承”的情況,則也要用實現(xiàn)Runnable接口的方式來處理多線程的問題。

P121 在JVM設(shè)置為Server服務(wù)器的環(huán)境中,線程會一直在私有堆棧中取得值為true,而thread.setRunning(false)更新的是公共堆棧的變量值false,所以會出現(xiàn)死循環(huán)。 問題是–私有堆棧中的值和公共堆棧中的值不同步造成的。 解決問題–要用volatile關(guān)鍵字 當(dāng)線程訪問isRunning時,強制性從公共堆棧中取值

這里寫圖片描述

public class RunThread extends Thread { volatile private boolean isRunning = true; public boolean isRunning(){ return isRunning; } public void setRunning(boolean isRunning){ this.isRunning = isRunning; } @Override public void run(){ System.out.println("進入run了"); while(isRunning == true){ } System.out.println("線程被停止了!"); }}public class Run { public static void main(String[] args) { try{ RunThread thread = new RunThread(); thread.start(); thread.sleep(1000); thread.setRunning(false); System.out.println("已經(jīng)賦值為false"); } catch (InterruptedException e){ e.printStackTrace(); } }}

volatile最致命的缺點是不支持原子性。

volatile和synchronized比較:

volatile是線程同步的輕量級實現(xiàn) volatile比synchronized性能好volatile只能修飾變量,synchronized可以修飾方法、代碼塊多線程訪問volatile不會發(fā)生阻塞,而synchronized會出現(xiàn)阻塞volatile能保證數(shù)據(jù)可見性,但不能保證原子性; synchronized可以保證原子性,也可間接保證可見性(因為它會將私有內(nèi)存和公共內(nèi)存中的數(shù)據(jù)做同步)volatile解決多個線程之間的可見性,而synchronized解決多個線程訪問資源的同步性。

volatile非原子的特性

線程安全包含原子性和可見性兩個方面。

volatile不具備同步性,也就不具備原子性。 主要用于多線程讀取共享變量時,獲取最新值使用。

注意:如果改變實例變量中的數(shù)據(jù),比如i++,也就是i=i+1 這樣的操作不是一個原子操作,也就是非線程安全。 步驟分解如下: 1. 從內(nèi)存中取出i的值 2. 計算i的值 3. 將i的值寫到內(nèi)存

假如在第二步計算值時,另一個線程也修改i的值,這時會出現(xiàn)“臟讀”,這是需要用synchronized來解決

這里寫圖片描述

多線程環(huán)境中,use和assign是多次出現(xiàn),這一操作不是原子性,所以在read和load之后,如果主內(nèi)存count變量發(fā)生修改之后,線程工作內(nèi)存中的值由于已經(jīng)加載,不會發(fā)生對應(yīng)的變化,也就是私有內(nèi)存和公共內(nèi)存的變量不同步,所以計算出來的和預(yù)期的不一樣,就出現(xiàn)非線程安全問題。

AtomicInteger原子類

一個原子類型就是一個原子操作可用的類型,它可以在沒有鎖的情況下做到線程安全。

public class AddCountThread extends Thread { private AtomicInteger count = new AtomicInteger(0); @Override public void run(){ for(int i = 0;i < 10000; i++) { System.out.println(count.incrementAndGet()); } }}public class Run { public static void main (String[] args) { AddCountThread countService = new AddCountThread(); Thread t1 = new Thread(countService); t1.start(); Thread t2 = new Thread(countService); t2.start(); Thread t3 = new Thread(countService); t3.start(); Thread t4 = new Thread(countService); t4.start(); Thread t5 = new Thread(countService); t5.start(); }}

運行結(jié)果成功累加到50000

原子類也并不完全安全

原子類在具有邏輯性的情況下輸出結(jié)果也具有隨機性。 - addAndGet()這些原子類的方法是原子的,但是方法與方法之間的調(diào)用不是原子的。

synchronized的特性:互斥性和可見性


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 瑞安市| 江达县| 通道| 林甸县| 县级市| 牙克石市| 阿瓦提县| 盐津县| 阳山县| 肇东市| 凤冈县| 庄河市| 德江县| 石河子市| 台山市| 北宁市| 万宁市| 大荔县| 迁西县| 确山县| 定日县| 什邡市| 西昌市| 阿瓦提县| 茌平县| 澄江县| 武宣县| 德化县| 邯郸市| 雷州市| 阿拉尔市| 连云港市| 同仁县| 星子县| 淳化县| 天祝| 白水县| 南丹县| 当阳市| 长丰县| 秦皇岛市|