1. 程式人生 > >java多執行緒系列(1)

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)上 桌子