多线程 JUC
2019獨角獸企業重金招聘Python工程師標準>>>
多線程 JUC
object.wait():
在其他線程調用此對象的notify()或者notifyAll()方法,或超過指定時間量前,當前線程T等待(線程T必須擁有該對象的鎖)。線程T被放置在該對象的等待區中,并釋放鎖。在被喚醒、中斷、超時的情況下,從對象的等待區中刪除線程T,并重新進行線程調度。一旦線程T獲得該對象的鎖,該對象上的所有同步申明都被恢復到調用wait()方法時的狀態,然后線程T從wait()方法返回。如果當前線程在等待之前或在等待時被任何線程中斷,則會拋出 InterruptedException。在按上述形式恢復此對象的鎖定狀態時才會拋出此異常。在拋出此異常時,當前線程的中斷狀態被清除。
object.notify()
喚醒在此對象鎖上等待的單個線程。此方法只能由擁有該對象鎖的線程來調用。
Thread.sleep()
在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和準確性的影響。監控狀態依然保持、會自動恢復到可運行狀態,不會釋放對象鎖。如果任何線程中斷了當前線程。當拋出InterruptedException異常時,當前線程的中斷狀態被清除。讓出CPU分配的執行時間。
thread.join():在一個線程對象上調用,使當前線程等待這個線程對象對應的線程結束之后繼續執行。
Thread.yield():暫停當前正在執行的線程對象,并執行其他線程(讓出cpu)。
thread.interrupt():中斷線程,停止其正在進行的一切。中斷一個不處于活動狀態的線程不會有任何作用。如果線程在調用Object類的wait()方法、或者join()、sleep()方法過程中受阻,則其中斷狀態將被清除,并收到一個InterruptedException。
Thread.interrupted():檢測當前線程是否已經中斷,并且清除線程的中斷狀態(回到非中斷狀態)。
Synchronized關鍵字:編譯后會在同步塊前后分別形成monitorenter和monitorexit這兩個字節碼指令。這兩個指令都需要一個引用類型的參數來指明要鎖定和解鎖的對象。如果沒有明確指定對象參數,那就根據synchronized修飾的是實例方法還是類方法,去取對應的對象實例或Class對象來作為鎖對象。在執行monitorenter指令時,首先嘗試獲取對象的鎖,如果沒有被鎖定或者當前線程已經擁有了該對象的鎖,則將鎖計數器加1,相應的執行moniterexit時,將鎖計數器減1,當計數器為0時,鎖就被釋放了。如果獲取對象鎖失敗,則當前線程就要阻塞等待。
?
在?Java?5.0 提供了?Java.util.concurrent (簡稱JUC )包,在此包中增加了在并發編程中很常用的實用工具類,用于定義類似于線程的自定義子系統,包括線程池、異步 IO 和輕量級任務框架。提供可調的、靈活的線程池。還提供了設計用于多線程上下文中的 Collection 實現等。
一、volatile關鍵字、內存可見性
內存可見性
內存可見性(Memory Visibility)是指當某個線程正在使用對象狀態而另一個線程在同時修改該狀態,需要確保當一個線程修改了對象狀態后,其他線程能夠看到發生的狀態變化。
可見性錯誤是指當讀操作與寫操作在不同的線程中執行時,我們無法確保執行讀操作的線程能適時地看到其他線程寫入的值,有時甚至是根本不可能的事情。
我們可以通過同步來保證對象被安全地發布。除此之外我們也可以使用一種更加輕量級的 volatile 變量。
volatile 關鍵字
Java 提供了一種稍弱的同步機制,即 volatile 變量,用來確保多個線程將變量的更新操作通知到其他線程,可以保證內存中的數據可見。可以將 volatile 看做一個輕量級的鎖,但是又與鎖有些不同:
- 對于多線程,不是一種互斥關系
- 不能保證變量狀態的“原子性操作”
public class TestVolatile {
??? public static void main(String[] args){
???? ???ThreadDemo td=new ThreadDemo();
??????? new Thread(td).start();
??????? while(true){
??????????? if(td.isFlag()){
??????????????? System.out.println("-----------");
??????????????? break;
??????????? }
??????? }
??? }
?
}
class ThreadDemo implements Runnable{
??? private volatile boolean flag=false;
??? public void run() {
??????? try {
??????????? Thread.sleep(200);
??????? } catch (InterruptedException e) {
??????????? e.printStackTrace();
??????? }
??????? flag=true;
??????? System.out.println("flag="+isFlag());
??? }
??? public boolean isFlag(){
??????? return flag;
??? }
??? public void setFlag(boolean flag){
??????? this.flag=flag;
??? }
}
二、原子變量 、CAS
原子變量:jdk1.5 后 java.util.concurrent.atomic 類的小工具包,支持在單個變量上解除鎖的線程安全編程,包下提供了常用的原子變量:?
- AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference?
- AtomicIntegerArray 、AtomicLongArray?
- AtomicMarkableReference?
- AtomicReferenceArray?
- AtomicStampedReference
1.類中的變量都是volatile類型:保證內存可見性?
2.使用CAS算法:保證數據的原子性
CAS (Compare-And-Swap) 是一種硬件對并發的支持,針對多處理器操作而設計的處理器中的一種特殊指令,用于管理對共享數據的并發訪問。?
CAS 是一種無鎖的非阻塞算法的實現。?
CAS包含三個操作數:?
內存值 V?
預估(舊)值 A?
更新值 B?
當且僅當V==A時,B的值才更新給A,否則將不做任何操作。
public class TestAtomicDemo {
?
??? public static void main(String[] args) {
??????? AtomicDemo ad = new AtomicDemo();
?
??????? for (int i = 0; i < 10; i++) {
??????????? new Thread(ad).start();
??????? }
??? }
?
}
?
class AtomicDemo implements Runnable{
?
//? private volatile int serialNumber = 0;
?
??? private AtomicInteger serialNumber = new AtomicInteger(0);
?
??? @Override
??? public void run() {
?
??????? try {
??????????? Thread.sleep(200);
??????? } catch (InterruptedException e) {
??????? }
?
??????? System.out.println(getSerialNumber());
??? }
?
public int getSerialNumber(){
// return serialNumber++;
??????? return serialNumber.getAndIncrement();//i++ 實際是int temp=i;i=i+1;i=temp; 需要原子性操作
??? }
}
使用synchronized方法模擬CAS 算法,實際是由硬件機制完成的,用10個線程代表對內存中數據的10次修改請求。只有上個線程修改完,這個線程從內存中獲取的內存值當成期望值,才等于內存值,才能對內存值進行修改。
public class TestCompareAndSwap {
??? public static void main(String[] args) {
??????? final CompareAndSwap cas=new CompareAndSwap();
?
??????? for(int i=0;i<10;i++){
??????????? new Thread(new Runnable(){
??????????????? @Override
??????????????? public void run() {
??????????????????? int expectedValue=cas.get();
??????????????????? boolean b=cas.compareAndSwap(expectedValue, (int)(Math.random()*101));
??????????????????? System.out.println(b);
??????????????? }
??????????? }).start();
??????? }
??? }
}
?
class CompareAndSwap{
??? private int value;//內存值
?
??? //獲取內存值
??? public synchronized int get(){
??????? return value;
??? }
?
??? //比較
??? public synchronized boolean compareAndSwap(int expectedValue,int newValue){
??????? int oldValue=value;//線程讀取內存值,與預估值比較
?????? ?if(oldValue==expectedValue){
??????????? this.value=newValue;
??????????? return true;
??????? }
??????? return false;
??? }
}
三、ConcurrentHashMap、鎖分段
HashMap 線程不安全?
Hashtable 內部采用獨占鎖,線程安全,但效率低?
ConcurrentHashMap同步容器類是java5 新增的一個線程安全的哈希表,效率介于HashMap和Hashtable之間。內部采用“鎖分段”機制。
java.util.concurrent 包還提供了設計用于多線程上下文中的Collection實現:
當期望許多線程訪問一個給定 collection 時,?
ConcurrentHashMap 通常優于同步的 HashMap,?
ConcurrentSkipListMap 通常優于同步的 TreeMap?
ConcurrentSkipListSet通常優于同步的 TreeSet.
當期望的讀數和遍歷遠遠大于列表的更新數時,?
CopyOnWriteArrayList 優于同步的 ArrayList。因為每次添加時都會進行復制,開銷非常的大,并發迭代操作多時 ,選擇。
四、CountDownLatch 閉鎖
CountDownLatch 一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。
閉鎖可以延遲線程的進度直到其到達終止狀態,閉鎖可以用來確保某些活動直到其他活動都完成才繼續執行:?
?確保某個計算在其需要的所有資源都被初始化之后才繼續執行;
?確保某個服務在其依賴的所有其他服務都已經啟動之后才啟動;?
?等待直到某個操作所有參與者都準備就緒再繼續執行。
/*
?* CountDownLatch:閉鎖,在完成某些運算時,只有其他所有線程的運算全部完成,當前運算才繼續執行
?*/
public class TestCountDownLatch {
??? public static void main(String[] args) {
??????? final CountDownLatch latch=new CountDownLatch(50);
??????? LatchDemo ld=new LatchDemo(latch);
?
??????? long start=System.currentTimeMillis();
?
??????? for(int i=0;i<50;i++){
??????????? new Thread(ld).start();
??????? }
?
??????? try {
??????????? latch.await(); //直到50個人子線程都執行完,latch的值減到0時,才往下執行
??????? } catch (InterruptedException e) {
???????? ???e.printStackTrace();
??????? }
?
??????? long end=System.currentTimeMillis();
??????? System.out.println("耗費時間為:"+(end-start));
??? }
}
?
class LatchDemo implements Runnable{
??? private CountDownLatch latch;
?
??? public LatchDemo(CountDownLatch latch){
??????? this.latch=latch;
??? }
??? @Override
??? public void run() {
??????? try{
??????????? for(int i=0;i<50000;i++){
??????????????? if(i%2==0){
??????????????????? System.out.println(i);
??????????????? }
??????????? }
??????? }finally{
??????????? latch.countDown();//latch的值減一
??????? }
??? }
}
五、實現Callable接口
java?5.0 在 java.util.concurrent 提供了一個新的創建執行線程的方式:Callable 接口
實現Callable 接口,相較于實現 Runnable接口的方式,方法可以有返回值,并且可以拋出異常。
Callable 需要依賴FutureTask ,用于接收返回值,FutureTask 也可以用作閉鎖。
/*
?* 一、創建執行線程的方式三:實現Callable接口。相較于實現Runnable接口的方式,方法可以有返回值,并且可以拋出異常。
?* 二、執行Callable方式,需要FutureTask實現類的支持,用于接收運算結果。FutureTask是Future接口的實現類
?*/
public class TestCallable {
??? public static void main(String[] args) {
??????? ThreadDemo2 td=new ThreadDemo2();
?
??????? //1.執行Callable方式,需要FutureTask實現類的支持,用于接收運行結果。
??????? FutureTask<Integer> result=new FutureTask<>(td);
??????? new Thread(result).start();
?
??????? //2.接收線程運算后的結果
??????? try {
??????????? Integer sum = result.get();//FutureTask 可用于 閉鎖? 當子線程執行完畢,才會執行此后語句
??????????? System.out.println(sum);
??????????? System.out.println("----------------------");
??????? } catch (InterruptedException | ExecutionException e) {
??????????? e.printStackTrace();
??????? }
??? }
}
?
class ThreadDemo2 implements Callable<Integer>{
?
??? @Override
??? public Integer call() throws Exception {
??????? int sum=0;
??????? for(int i=0;i<=100000;i++){
??????????? sum+=i;
??????? }
??????? return sum;
??? }
?
}
六、Lock 同步鎖
在 Java 5.0 之前,協調共享對象的訪問時可以使用的機制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的機制,但并不是一種替代內置鎖的方法,而是當內置鎖不適用時,作為一種可選擇的高級功能。
ReentrantLock 實現了 Lock 接口,并提供了與synchronized 相同的互斥性和內存可見性。但相較于synchronized 提供了更高的處理鎖的靈活性。
/*
?* 一、用于解決多線程安全問題的方式:
?* synchronized:隱式鎖
?* 1、同步代碼塊
?* 2、同步方法
?* jdk 1.5后
?* 3、同步鎖 Lock?
?* 注意:是一個顯式鎖,通過lock()方式上鎖,必須通過unlock()方法釋放鎖
?*/
?
/*
?* 賣票
?*/
public class TestLock {
??? public static void main(String[] args) {
??????? Ticket ticket=new Ticket();
??????? new Thread(ticket,"1號窗口").start();
??????? new Thread(ticket,"2號窗口").start();
??????? new Thread(ticket,"3號窗口").start();
??? }
}
?
class Ticket implements Runnable{
??? private int tick=100;
??? private Lock lock=new ReentrantLock();
??? @Override
??? public void run() {
??????? while(true){
??????????? lock.lock();
?? ?????????try{
??????????????? if(tick>0){
??????????????????? try {
??????????????????????? Thread.sleep(200);
??????????????????? } catch (InterruptedException e) {
??????????????????????? e.printStackTrace();
??????????????????? }
??????????????????? System.out.println(Thread.currentThread().getName()+"完成售票為:"+--tick);
??????????????? }
??????????????? else{
??????????????????? break;
??????????????? }
??????????? }finally{
??????????????? lock.unlock();//釋放鎖一定要放在finally里,保證一定執行
??????????? }
??????? }
??? }
?
}
?
?
?
/*
?* 生產者和消費者案例,優化,防止出現虛假喚醒,線程無法停止
?*/
public class TestProductorAndConsumer {
??? public static void main(String[] args) {
??????? Clerk clerk=new Clerk();
?
??????? Productor pro=new Productor(clerk);
??????? Consumer cus=new Consumer(clerk);
?
??????? new Thread(pro,"生產者 A").start();
??????? new Thread(cus,"消費者 B").start();
??????? new Thread(pro,"生產者 C").start();
??????? new Thread(cus,"消費者 D").start();
??? }
}
?
//店員 假如只有一個商品位置
class Clerk{
??? private int product=0;
?
??? //進貨
??? public synchronized void get(){
??????? while(product>=1){//為了避免虛假喚醒問題,應該總是使用在循環中
??????????? System.out.println("產品已滿!");
??????????? try{
??????????????? this.wait();
??????????? }catch(InterruptedException e){
??????????? }
??????? }
??????? System.out.println(Thread.currentThread().getName()+" : "+ ++product);
?? ?????this.notifyAll();
??? }
?
??? //賣貨
??? public synchronized void sale(){
??????? while(product<=0){ //為了避免虛假喚醒問題,應該總是使用在循環中
??????????? System.out.println("缺貨!");
??????????? try {
??????????????? this.wait();
??????????? } catch (InterruptedException e) {
??????????????? e.printStackTrace();
??????????? }
??????? }
??????? System.out.println(Thread.currentThread().getName()+" : "+ --product);
??????? this.notifyAll();
??? }
}
?
//生產者
class Productor implements Runnable{
??? private Clerk clerk;
??? public Productor(Clerk clerk){
??????? this.clerk=clerk;
??? }
??? @Override
??? public void run() {
??????? for(int i=0;i<20;i++){
??????????? try{
??????????????? Thread.sleep(100);
??????????? }catch(InterruptedException e){
??????????? }
??????????? clerk.get();
??????? }
??? }
}
?
//消費者
class Consumer implements Runnable{
??? private Clerk clerk;
??? public Consumer(Clerk clerk){
??????? this.clerk=clerk;
??? }
??? @Override
??? public void run() {
??????? for(int i=0;i<20;i++){
??????????? clerk.sale();
??????? }
??? }
}
?
七、Condition 控制線程通信
Condition 接口描述了可能會與鎖有關聯的條件變量。這些變量在用法上與使用 Object.wait 訪問的隱式監視器類似,但提供了更強大的功能。需要特別指出的是,單個 Lock 可能與多個 Condition 對象關聯。為了避免兼容性問題,Condition 方法的名稱與對應的 Object 版本中的不同。
在 Condition 對象中,與 wait、notify 和 notifyAll 方法對應的分別是await、signal 和 signalAll。
Condition 實例實質上被綁定到一個鎖上。要為特定 Lock 實例獲得Condition 實例,請使用其 newCondition() 方法。
public class TestProductorAndConsumerForLock {
??? public static void main(String[] args) {
??????? Clerk clerk=new Clerk();
?
??????? Productor pro=new Productor(clerk);
??????? Consumer cus=new Consumer(clerk);
?
??????? new Thread(pro,"生產者 A").start();
??????? new Thread(cus,"消費者 B").start();
??????? new Thread(pro,"生產者 C").start();
??????? new Thread(cus,"消費者 D").start();
??? }
}
?
//店員 假如只有一個商品位置
class Clerk{
??? private int product=0;
??? private Lock lock=new ReentrantLock();
??? private Condition condition=lock.newCondition();
?
??? //進貨
??? public void get(){
??????? lock.lock();
?
??????? try{
??????????? while(product>=1){//為了避免虛假喚醒問題,應該總是使用在循環中
??????????????? System.out.println("產品已滿!");
? ??????????????try{
??????????????????? condition.await();//this.wait();
??????????????? }catch(InterruptedException e){
??????????????? }
??????????? }
??????????? System.out.println(Thread.currentThread().getName()+" : "+ ++product);
??????????? condition.signalAll();//this.notifyAll();
??????? }finally{
??????????? lock.unlock();
??????? }
??? }
?
??? //賣貨
??? public void sale(){
??????? lock.lock();
?
??????? try{
??????????? while(product<=0){
??????????????? System.out.println("缺貨!");
??????????????? try {
??????????????????? condition.await();//this.wait();
??????????????? } catch (InterruptedException e) {
??????????????????? e.printStackTrace();
??????????????? }
??????????? }
??????????? System.out.println(Thread.currentThread().getName()+" : "+ --product);
??????????? condition.signalAll();//this.notifyAll();
??????? }finally{
??????????? lock.unlock();
??????? }
??? }
}
?
//生產者
class Productor implements Runnable{
??? private Clerk clerk;
??? public Productor(Clerk clerk){
??????? this.clerk=clerk;
??? }
??? @Override
??? public void run() {
??????? for(int i=0;i<20;i++){
??????????? try{
???????? ???????Thread.sleep(100);
??????????? }catch(InterruptedException e){
??????????? }
??????????? clerk.get();
??????? }
??? }
}
?
//消費者
class Consumer implements Runnable{
??? private Clerk clerk;
??? public Consumer(Clerk clerk){
??????? this.clerk=clerk;
??? }
??? @Override
??? public void run() {
??????? for(int i=0;i<20;i++){
??????????? clerk.sale();
??????? }
??? }
}
八、線程按序交替
編寫一個程序,開啟 3 個線程,這三個線程的 ID 分別為A、B、C,每個線程將自己的 ID 在屏幕上打印 10 遍,要求輸出的結果必須按順序顯示。如:ABCABCABC…… 依次遞歸
public class TestABCAlternate {
??? public static void main(String[] args) {
??????? AlternateDemo ad=new AlternateDemo();
??????? new Thread(new Runnable(){
??????????? @Override
??????????? public void run() {
??????????????? for(int i=1;i<=20;i++){
??????????????????? ad.loopA(i);
?????? ?????????}
??????????? }
?
??????? },"A").start();
?
??????? new Thread(new Runnable(){
??????????? @Override
??????????? public void run() {
??????????????? for(int i=1;i<=20;i++){
??????????????????? ad.loopB(i);
??????????????? }
??????????? }
?
??????? },"B").start();
?
??????? new Thread(new Runnable(){
??????????? @Override
??????????? public void run() {
??????????????? for(int i=1;i<=20;i++){
??????????????????? ad.loopC(i);
??????????????????? System.out.println("-----------------------------------");
??????????????? }
??????????? }
?
??????? },"C").start();
??? }
}
?
class AlternateDemo{
??? private int number=1;//當前正在執行線程的標記
??? private Lock lock=new ReentrantLock();
??? private Condition condition1=lock.newCondition();
??? private Condition condition2=lock.newCondition();
??? private Condition condition3=lock.newCondition();
?
??? /*
???? * @param totalLoop:循環第幾輪
???? */
??? public void loopA(int totalLoop){
??????? lock.lock();
??????? try{
??????????? //1.判斷
??????????? if(number!=1){
??????????????? condition1.await();
??????????? }
?
??????????? //2.打印
??????????? System.out.println(Thread.currentThread().getName()+"\t"+totalLoop);
?
??????????? //3.喚醒
??????????? number=2;
??????????? condition2.signal();
??????? } catch (InterruptedException e) {
??? ????????e.printStackTrace();
??????? } finally{
??????????? lock.unlock();
??????? }
??? }
?
??? public void loopB(int totalLoop){
??????? lock.lock();
??????? try{
??????????? //1.判斷
??????????? if(number!=2){
??????????????? condition2.await();
????????? ??}
?
??????????? //2.打印
??????????? System.out.println(Thread.currentThread().getName()+"\t"+totalLoop);
?
??????????? //3.喚醒
??????????? number=3;
??????????? condition3.signal();
??????? } catch (InterruptedException e) {
??????????? e.printStackTrace();
??????? } finally{
??????????? lock.unlock();
??????? }
??? }
?
??? public void loopC(int totalLoop){
??????? lock.lock();
??????? try{
??????????? //1.判斷
??????????? if(number!=3){
??????????????? condition3.await();
??????????? }
?
??????????? //2.打印
???? ???????System.out.println(Thread.currentThread().getName()+"\t"+totalLoop);
?
??????????? //3.喚醒
??????????? number=1;
??????????? condition1.signal();
??????? } catch (InterruptedException e) {
??????????? e.printStackTrace();
??????? } finally{
????????? ??lock.unlock();
??????? }
??? }
}
九、ReadWriteLock 讀寫鎖
ReadWriteLock 維護了一對相關的鎖,一個用于只讀操作,另一個用于寫入操作。只要沒有 writer,讀取鎖可以由多個 reader 線程同時保持。寫入鎖是獨占的。
ReadWriteLock 讀取操作通常不會改變共享資源,但執行寫入操作時,必須獨占方式來獲取鎖。對于讀取操作占多數的數據結構。 ReadWriteLock 能提供比獨占鎖更高的并發性。而對于只讀的數據結構,其中包含的不變性可以完全不需要考慮加鎖操作。
/*
?* 1.ReadWriteLock:讀寫鎖
?* 寫寫|讀寫?? 需要"互斥"
?* 讀讀?????? 不需要"互斥"
?*/
public class TestReadWriteLock {
??? public static void main(String[] args) {
??????? ReadWriteLockDemo rw=new ReadWriteLockDemo();
??????? new Thread(new Runnable() {
??????????? @Override
??????????? public void run() {
??????????????? rw.set((int)(Math.random()*101));
??????????? }
??????? },"Write").start();
?
??????? for(int i=0;i<100;i++){
??????????? new Thread(new Runnable() {
??????????????? @Override
??????????????? public void run() {
??????????????????? rw.get();
??????????????? }
??????????? }).start();
??????? }
??? }
}
?
class ReadWriteLockDemo{
??? private int number=0;
private ReadWriteLock lock=new ReentrantReadWriteLock();
private readLock = lock. readLock();
private writeLock = lock. writeLock();
?
?
?
??? //讀
??? public void get(){
??????? readLock.lock();//上鎖
??????? try{
??????????? System.out.println(Thread.currentThread().getName()+" : "+number);
??????? }finally{
??????????? readLock.unlock();//釋放鎖
??????? }
??? }
?
??? //寫
??? public void set(int number){
??????? writeLock.lock();
??????? try{
??????????? System.out.println(Thread.currentThread().getName());
??????????? this.number=number;
??????? }finally{
??????????? writeLock.unlock();
??????? }
??? }
}
十、線程八鎖
- 一個對象里面如果有多個synchronized實例方法,某一個時刻內,只要一個線程去調用其中的一個synchronized實例方法了,其它的線程都只能等待,換句話說,某一個時刻內,只能有唯一一個線程去訪問這些synchronized實例方法。鎖的是當前對象this,被鎖定后,其它的線程都不能進入到當前對象的其它的synchronized實例方法。
- 加個普通方法后發現和同步鎖無關
- 換成兩個對象后,不是同一把鎖了,情況立刻變化。
- 都換成靜態同步方法后,情況又變化
- 所有的非靜態同步方法用的都是同一把鎖——實例對象本身,也就是說如果一個實例對象的非靜態同步方法獲取鎖后,該實例對象的其他非靜態同步方法必須等待獲取鎖的方法釋放鎖后才能獲取鎖,可是別的實例對象的非靜態同步方法因為跟該實例對象的非靜態同步方法用的是不同的鎖,所以毋須等待該實例對象已獲取鎖的非靜態同步方法釋放鎖就可以獲取他們自己的鎖。
- 所有的靜態同步方法用的也是同一把鎖——類對象本身,這兩把鎖是兩個不同的對象,所以靜態同步方法與非靜態同步方法之間是不會有競態條件的。但是一旦一個靜態同步方法獲取鎖后,其他的靜態同步方法都必須等待該方法釋放鎖后才能獲取鎖,而不管是同一個實例對象的靜態同步方法之間,還是不同的實例對象的靜態同步方法之間,只是它們同一個類的實例對象!
/*
?* 實驗:觀察打印的"one" or "two" ?
?*
?* 1.兩個普通同步方法,兩個線程,標準打印,打印?? //one two? 因為同步鎖是this(調用對象本身),被鎖定后,其它的線程都不能進入到當前對象的其它的synchronized方法
?* 2.新增Thread.sleep()給 getOne(),打印? //等3秒后? one two
?* 3.新增普通方法(非同步) getThree(),打印?//three 等3秒 one two 因為同步鎖不影響普通方法的執行
?* 4.兩個普通同步方法,兩個Number對象,打印?//two 等3秒 one? 因為用的不是同一把鎖,互不影響
?* 5.修改 getOne() 為靜態同步方法,使用一個Number對象打印?? //two 等3秒 one? 因為靜態同步方法用的鎖是類對象本身,Number.class; 和對象用的是不同的鎖
?* 6.修改兩個方法均為靜態同步方法,一個Number對象?//等3秒 one two 用的鎖都是Number類對象本身
?* 7.一個靜態同步方法,一個非靜態同步方法,兩個Number對象?//two 等3秒one
?* 8.兩個靜態同步方法,兩個Number對象?//等3秒后? one two 用的鎖都是Number類對象本身
?*
?* 線程八鎖的關鍵:
?* ①非靜態方法的鎖默認為? this,? 靜態方法的鎖為 對應的 Class 實例
?* ②某一個時刻內,只能有一個線程持有同一把鎖,無論幾個方法。
?*/
?
public class TestThread8Monitor {
??? public static void main(String[] args) {
??????? Number number = new Number();
??????? Number number2 = new Number();
??????? new Thread(new Runnable() {
??????????? public void run() {
??????????????? number.getOne();
??????????? }
??????? }).start();
??????? new Thread(new Runnable() {
??????????? public void run() {
//????????????? number.getTwo();
??????????????? number2.getTwo();
??????????? }
??????? }).start();
//? ????new Thread(new Runnable() {
//????????? public void run() {
//????????????? number.getThree();
//????????? }
//????? }).start();
?
??? }
?
}
?
class Number {
??? public static synchronized void getOne() {
??????? try {
??????????? Thread.sleep(3000);
??? ????} catch (InterruptedException e) {
??????? }
??????? System.out.println("one");
??? }
?
??? public static synchronized void getTwo() {
??????? System.out.println("two");
??? }
?
//? public void getThree(){
//????? System.out.println("three");
//? }
}
?
?
十一、線程池
獲取線程第四種方法。?
線程池可以解決兩個不同問題:由于減少了每個任務調用的開銷,它們通常可以在執行大量異步任務時提供增強的性能,并且還可以提供綁定和管理資源(包括執行任務集時使用的線程)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計數據,如完成的任務數。
為了便于跨大量上下文使用,此類提供了很多可調整的參數和擴展鉤子 (hook)。但是,強烈建議程序員使用較為方便的 Executors 工廠方法 :
Executors.newCachedThreadPool()(無界線程池,可以進行自動線程回收)
Executors.newFixedThreadPool(int)(固定大小線程池)
Executors.newSingleThreadExecutor()(單個后臺線程)
它們均為大多數使用場景預定義了設置。
/*
?* 一、線程池:提供了一個線程隊列,隊列中保存著所有等待狀態的線程。避免了創建與銷毀額外開銷,提高了響應的速度。
?* 二、線程池的體系結構:
?*??? java.util.concurrent.Executor:負責線程的使用與調度的根接口
?*??? ????|--**ExecutorService 子接口:線程池的主要接口
?*???????????? |--ThreadPoolExecutor 線程池的實現類
?*???????????? |--ScheduledExecutorService 子接口:負責線程的調度
?*????????????????? |--ScheduledThreadPoolExecutor:繼承ThreadPoolExecutor,實現ScheduledExecutorService接口
?* 三、工具類:Executors
?* 方法有:
?* ExecutorService newFixedThreadPool(): 創建固定大小的線程池
?* ExecutorService newCachedThreadPool():緩存線程池,線程池的數量不固定,可以根據需要自動的更改數量。
?* ExecutorService newSingleThreadExecutor():創建單個線程池。線程池中只有一個線程
?*
?* ScheduledExecutorService newScheduledThreadPool():創建固定大小的線程,可以延遲或定時的執行任務。
?*
?*/
public class TestThreadPool {
??? public static void main(String[] args) {
??????? //1.創建線程池
??????? ExecutorService pool=Executors.newFixedThreadPool(5);
?
??????? List<Future<Integer>> list=new ArrayList<>();
??????? for (int i = 0; i < 10; i++) {
??????????? Future<Integer> future=pool.submit(new Callable<Integer>(){
??????????????? @Override
??????????????? public Integer call() throws Exception {
??????????????????? int sum=0;
??????????????????? for(int i=0;i<=100;i++){
??????????????????????? sum+=i;
??????????????????? }
??????????????????? return sum;
??????????????? }
?
??????????? });
??????????? list.add(future);
??????? }
??????? pool.shutdown(); // 任務執行完畢關閉
??????? for(Future<Integer> future:list){
??????????? System.out.println(future.get());
??????? }
??? }
}
十二、線程調度
public class TestScheduledThreadPool {
??? public static void main(String[] args) throws Exception {
??????? ScheduledExecutorService pool=Executors.newScheduledThreadPool(5);
??????? for (int i = 0; i < 5; i++) {
??????????? Future<Integer> result=pool.schedule(new Callable<Integer>() {
??????????????? @Override
??????????????? public Integer call() throws Exception {
??????????????????? int num=new Random().nextInt(100);//生成隨機數
??????????????????? System.out.println(Thread.currentThread().getName()+" : "+num);
??????????????????? return num;
??????????????? }
?
??????????? }, 2, TimeUnit.SECONDS);//每次延遲兩秒后運行
??????????? System.out.println(result.get());
??????? }
??? }
}
?
轉載于:https://my.oschina.net/dslcode/blog/1591214
總結
- 上一篇: jQuery 效果 - animate(
- 下一篇: Nginx+Keeplived双机热备(