java多線程系列(1)

1,爲何須要線程?java

做用:提高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函數的名字相同而已。dom

3,在java中實現多線程有2種方式異步

>繼承Thread類函數

>實現Runnable接口this

在run方法中寫線程要執行的任務spa

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();
        }
    }
相關文章
相關標籤/搜索