java多執行緒系列(1)
1,為什麼需要執行緒?
作用:提升cpu的利用率,如,早期的dos系統,執行2個命令時( command 1, command 2 ),如果command1【假如是磁碟遍歷檔案的IO操作】執行的時間比較長,那麼command 2必須等待,這種方式就是同步阻塞,
cpu就閒置了,為了提高cpu的利用率,我們就要使用多執行緒,如果一個任務時間比較長,cpu就暫時掛起他,去執行另外的執行緒,所以執行緒一般是非同步的。
2,每一個程序至少會有一個執行緒在執行
public class Test { public static void main(String[] args) {//列印執行緒的名稱 System.out.println( Thread.currentThread().getName() ); } }
輸出結果為 "main" ,注意這個main是執行緒的名字,跟main函式的名字相同而已。
3,在java中實現多執行緒有2種方式
>繼承Thread類
>實現Runnable介面
在run方法中寫執行緒要執行的任務
class MyThread extends Thread{ public void run(){ System.out.println("MyThread::run" ); } } public class ThreadUse1 { public static void main(String[] args) { MyThread mt = new MyThread(); mt.start(); System.out.println( "執行結束" ); } }
從執行結果可知,run方法是在之後執行的,雖然start開啟執行緒比 【System.out.println( "執行結束" );】 他早,這說明,CPU在呼叫執行緒的時候,是隨機的
4,再次驗證cpu呼叫執行緒的隨機性
class MyThreadRand extends Thread{ public void run(){ try{ for ( int i = 0; i < 10; i++ ) { int time = ( int )( Math.random() * 1000 ); Thread.sleep( time ); System.out.println( "MyThread:" + Thread.currentThread().getName() ); } }catch( InterruptedException e ){ e.printStackTrace(); } } } public class RandThread { public static void main(String[] args) { try{ MyThreadRand mt = new MyThreadRand(); mt.setName( "自定義執行緒" ); mt.start(); for ( int i = 0; i < 10; i++ ) { int time = ( int )( Math.random() * 1000 ); Thread.sleep( time ); System.out.println( "MainThread:" + Thread.currentThread().getName() ); } }catch( InterruptedException e ){ e.printStackTrace(); } } }
從執行結果可知,執行緒的排程沒有什麼規律,是隨機的, 這裡補充一點,start方法作用是通知 “執行緒規劃器”,這個執行緒已經準備好了,等待呼叫執行緒的run方法,就是讓系統安排一個時間來呼叫run方法。如果直接呼叫run方法,執行緒就變成同步方式了,必須等待MyThreadRand的run方法執行完成之後,才會執行main函式中的執行緒
5,start方法的順序,不代表執行緒的啟動順序
class MyThreadStart extends Thread{ private int i; public MyThreadStart( int i ) { this.i = i; } public void run(){ System.out.println( i ); } } public class RandThread2 { public static void main(String[] args) { MyThreadStart s1 = new MyThreadStart( 1 ); MyThreadStart s2 = new MyThreadStart( 2 ); MyThreadStart s3 = new MyThreadStart( 3 ); MyThreadStart s4 = new MyThreadStart( 4 ); MyThreadStart s5 = new MyThreadStart( 5 ); MyThreadStart s6 = new MyThreadStart( 6 ); MyThreadStart s7 = new MyThreadStart( 7 ); MyThreadStart s8 = new MyThreadStart( 8 ); MyThreadStart s9 = new MyThreadStart( 9 ); MyThreadStart s10 = new MyThreadStart( 10 ); s1.start(); s2.start(); s3.start(); s4.start(); s5.start(); s6.start(); s7.start(); s8.start(); s9.start(); s10.start(); } }
6,實現Runnable介面
class MyThreadRunnable implements Runnable { public void run(){ System.out.println( Thread.currentThread().getName() ); } } public class ThreadRunnable { public static void main(String[] args) { MyThreadRunnable mt = new MyThreadRunnable(); Thread t = new Thread( mt ); t.setName( "自定義執行緒1" ); t.start(); } }
那麼兩種多執行緒的實現方式,有什麼不同呢?
>繼承Thread類
>實現Runnable介面
1,使用繼承Thread類的方式,多執行緒之間的資料不共享
class MyThreadShare extends Thread{ private int count = 5; public MyThreadShare( String name ){ this.setName( name ); } public void run(){ while( count-- > 0 ){ System.out.println( Thread.currentThread().getName() + "->" + count ); } } } public class ThreadShare { public static void main(String[] args) { MyThreadShare mt1 = new MyThreadShare( "A" ); MyThreadShare mt2 = new MyThreadShare( "B" ); MyThreadShare mt3 = new MyThreadShare( "C" ); mt1.start(); mt2.start(); mt3.start(); } }
2,而要想實現執行緒之間的資料共享,我們可以改一下
備註:執行緒資料共享與不共享,都有對應的場景,比如火車站4個視窗賣票,很顯然需要執行緒共享資料。如:總共用10張票,如果視窗賣了1張,其他視窗就指剩下9張,這才是比較貼近實際的,如果用第一種方式,相當於有40張餘票了。
class MyThreadShare2 extends Thread{ private int count = 5; public void run(){ while( count-- > 0 ){ System.out.println( Thread.currentThread().getName() + "->" + count ); } } } public class ThreadShare2 { public static void main(String[] args) { MyThreadShare2 mt = new MyThreadShare2(); Thread ta = new Thread( mt, "A" ); Thread tb = new Thread( mt, "B" ); Thread tc = new Thread( mt, "C" ); ta.start(); tb.start(); tc.start(); } }
從結果上看,好像實現了,資料共享,但是有點異常,B->3 很明顯不對,這種現象,在多執行緒程式設計裡面,叫“執行緒非安全”。現實生活中也有類似場景,比如4S店賣車,兩個客戶同時預訂了這輛車。那估計少不了一番辯論。怎麼解決這個問題呢?一般來說,在客戶訂車之前,銷售員要先檢視庫存,如果客戶下單,要把庫存佔用。表明有人預訂,其他銷售員看見了,就知道車被預訂了。程式中也是類似。如果要訪問這個變數,我們就給他加鎖,類似於銷售員佔用庫存。在方法前加上synchronized關鍵字。那麼其他執行緒訪問的時候,必須拿到這把鎖,才能訪問。synchronized可以在任意物件或者方法上加鎖。
class MyThreadShare2 extends Thread{ private int count = 5; // public void run(){ //產生執行緒非安全問題 synchronized public void run(){ while( count-- > 0 ){ System.out.println( Thread.currentThread().getName() + "->" + count ); } } } public class ThreadShare2 { public static void main(String[] args) { MyThreadShare2 mt = new MyThreadShare2(); Thread ta = new Thread( mt, "A" ); Thread tb = new Thread( mt, "B" ); Thread tc = new Thread( mt, "C" ); ta.start(); tb.start(); tc.start(); } }
3,模擬使用者登入場景,如果有兩個使用者登入,我們讓其中一個使用者執行緒佔時掛起。看下會出現什麼情況
class Login { private static String userName; private static String userPwd; public static void doPost( String _userName, String _userPwd ){ try { userName = _userName; if( userName.equals( "ghostwu" ) ) { Thread.sleep( 3000 ); } userPwd = _userPwd; System.out.println( userName + "---->" + userPwd ); }catch( InterruptedException e ){ e.printStackTrace(); } } } class ThreadA extends Thread{ public void run(){ Login.doPost( "ghostwu", "abc123" ); } } class ThreadB extends Thread{ public void run(){ Login.doPost( "ghostwuB", "abc1234" ); } } public class UserLogin { public static void main(String[] args) { ThreadA ta = new ThreadA(); ThreadB tb = new ThreadB(); ta.start(); tb.start(); } }
在A執行緒掛起的時候,他之前的賦值已經被B執行緒改變了,所以結果與預想的ghostwu abc123不同。很明顯,我們要上鎖。
synchronized public static void doPost( String _userName, String _userPwd ){ try { userName = _userName; if( userName.equals( "ghostwu" ) ) { Thread.sleep( 3000 ); } userPwd = _userPwd; System.out.println( userName + "---->" + userPwd ); }catch( InterruptedException e ){ e.printStackTrace(); } }
相關推薦
java多執行緒系列(1)
1,為什麼需要執行緒? 作用:提升cpu的利用率,如,早期的dos系統,執行2個命令時( command 1, command 2 ),如果command1【假如是磁碟遍歷檔案的IO操作】執行的時間比較長,那麼command 2必須等待,這種方式就是同步阻塞, cpu就閒置了,為了提高cpu的利用率,我們
Java多執行緒系列--“JUC原子類”03之 AtomicLong原子類
轉自:https://www.cnblogs.com/skywang12345/p/3514593.html(含部分修改) 概要 AtomicInteger, AtomicLong和AtomicBoolean這3個基本型別的原子類的原理和用法相似。本章以AtomicLong對基本型別的原子類進行介紹。內容
Java多執行緒系列---“JUC原子類”04之 AtomicLongArray原子類
轉自:https://www.cnblogs.com/skywang12345/p/3514604.html(含部分修改) 概要 AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray這3個數組型別的原子類的原理和用法相似。本章以AtomicLo
Java多執行緒系列---“JUC原子類”05之 AtomicReference原子類
轉自:http://www.cnblogs.com/skywang12345/p/3514623.html(部分修改) 概要 本章對AtomicReference引用型別的原子類進行介紹。內容包括: AtomicReference介紹和函式列表 AtomicReference原始碼分析(基於J
Java多執行緒系列---“JUC原子類”06之 AtomicLongFieldUpdater原子類
轉自:http://www.cnblogs.com/skywang12345/p/3514635.html (含部分修改) 概要 AtomicIntegerFieldUpdater, AtomicLongFieldUpdater和AtomicReferenceFieldUpdater這3個修改類的成員的原
Java多執行緒系列---“JUC原子類”01之 原子類的實現(CAS演算法)
轉自:https://blog.csdn.net/ls5718/article/details/52563959 & https://blog.csdn.net/mmoren/article/details/79185862(含部分修改) 在JDK 5之前Java語言是靠
Java多執行緒系列---“JUC原子類”02之 框架
轉自:http://www.cnblogs.com/skywang12345/p/3514589.html 根據修改的資料型別,可以將JUC包中的原子操作類可以分為4類。 1. 基本型別: AtomicInteger, AtomicLong, AtomicBoolean ;2.&
Java多執行緒系列---“JUC鎖”01之 框架
轉自:http://www.cnblogs.com/skywang12345/p/3496098.html(含部分修改) 本章,我們介紹鎖的架構;後面的章節將會對它們逐個進行分析介紹。目錄如下: 01. Java多執行緒系列--“JUC鎖”01之 框架 02. 
Java多執行緒系列---“JUC鎖”02之 ReentrantLock
轉自:https://www.jianshu.com/p/96c89e6e7e90 & https://blog.csdn.net/Somhu/article/details/78874634 (含部分修改) 一.ReentrantLock鎖 1.Lock介面 Lock,鎖
java多執行緒系列翻譯之java併發/多執行緒教程
原文地址:http://tutorials.jenkov.com/java-concurrency/index.html 以前計算機都是單核,同時只能執行一個程式。之後出現了多重任務處理,這意味著計算機同時可以處理多個程式(又名任務或流程)。但這不是真正的“同時執行”,只是單個CPU被多個程式共
Java多執行緒系列---“基礎篇”14之 wait,sleep,join,yield,park,unpark,notify等通訊機制對比
1. 執行緒讓步: yield() yield()的作用是讓步。它能讓當前執行緒由“執行狀態”進入到“就緒狀態”,從而讓其它具有相同優先順序的等待執行緒獲取執行權;但是,並不能保證在當前執行緒呼叫yield()之後,其它具有相同優先順序的執行緒就一定能獲得執行權;也有可能是當前執行緒又進入到“執行狀態”繼續
Java多執行緒系列---“JUC鎖”06之 公平鎖(下)
轉自:http://www.cnblogs.com/skywang12345/p/3496609.html 釋放公平鎖(基於JDK1.7.0_40) 1. unlock() unlock()在ReentrantLock.java中實現的,原始碼如下: public void unlock() {
java多執行緒:1程序和執行緒
多執行緒樣例 我們用工人卸貨舉例:有一集裝箱的貨物等待卸車,共100個箱子,一個工人一次只能搬一個箱子。 如果只有一個工人,那麼該工人需要搬運100次,而且是不停歇的搬運。 如果有5個或者10個工人,那麼平均每個工人只需要搬運20或者10次就可以了。 甚至有1
java多執行緒系列(一):Thread、Runnable、Callable實現多執行緒的區別
實現多執行緒 java實現多執行緒的方法有三種,分別是繼承thread類,實現runnable介面,實現callable介面(call方法有返回值) /** * 繼承Thread */ public class MyThread extends Thread{ int a = 0;
Java多執行緒2.1.多執行緒之死鎖
多執行緒之死鎖 1、鎖物件Lock 雖然我們可以理解同步程式碼塊和同步方法的鎖物件問題,但是我們並沒有直接看到在哪裡加上了鎖,在哪裡釋放了鎖。 為了更清晰的表達如何加鎖和釋放鎖,JDK5以後提
java多執行緒系列:通過對戰遊戲學習CyclicBarrier
CyclicBarrier是java.util.concurrent包下面的一個工具類,字面意思是可迴圈使用(Cyclic)的屏障(Barrier),通過它可以實現讓一組執行緒到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個執行緒到達屏障時,所有被屏障攔截的執
Java多執行緒系列--“JUC執行緒池”01之 執行緒池架構
概要 前面分別介紹了”Java多執行緒基礎”、”JUC原子類”和”JUC鎖”。本章介紹JUC的最後一部分的內容——執行緒池。內容包括: 執行緒池架構圖 執行緒池示例 執行緒池架構圖 執行緒池的架構圖如下: 1、Executor
Java多執行緒系列--“JUC執行緒池”05之 執行緒池原理(四)
概要 本章介紹執行緒池的拒絕策略。內容包括: 拒絕策略介紹 拒絕策略對比和示例 拒絕策略介紹 執行緒池的拒絕策略,是指當任務新增到執行緒池中被拒絕,而採取的處理措施。 當任務新增到執行緒池中之所以被拒絕,可能是由於:第一,執行緒池異常關閉。第二,任務數量
java多執行緒系列之模式|第一篇-Guarded Suspension pattern
Guarded Suspension pattern模式 作者注:該系列文章基於《java執行緒設計模式》撰寫,只用於學習和交流。 定義:多執行緒執行,當前執行緒沒有達到警戒條件時,執行緒會進入等待直到
java多執行緒系列之模式|第三篇: Producer-Consumer pattern
生產者-消費者模式 含義:顧名思義,生產者用來生產資料,可能有一到多個,消費者用來消費資料,也可能有多個,中間會有一個“橋樑參與者”,作為資料的存放以及執行緒之間的同步和協調。 範例程式行為: 廚師(MakerThread)做蛋糕,做好後放在桌子(Table)上 桌子