java基礎之多線程

java基礎之多線程

1. 多線程概述

1.1 線程與進程

  進程:進程指正在運行的程序。確切的來講,當一個程序進入內存運行,即變成一個進程,進程是處於運行過程當中的程序,而且每一個進程都具備必定獨立功能。
  線程:線程是進程中的一個執行單元,來完成進程中的某個功能。負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是能夠有多個線程的,這個應用程序也能夠稱之爲多線程程序。
  簡而言之:一個程序運行後至少有一個進程,一個進程中能夠包含多個線程。多線程

1.2 程序運行原理

  • 分時調度
    全部線程輪流使用 CPU 的使用權,平均分配每一個線程佔用 CPU 的時間。
  • 搶佔式調度
    優先讓優先級高的線程使用 CPU,若是線程的優先級相同,那麼會隨機選擇一個(線程隨機性),Java使用的爲搶佔式調度

2. 線程的建立和啓動

2.1 Thread類

  繼承Thread類建立線程併發

  • 定義Thread類的子類,並重寫該類的run()方法,該run()方法的方法體就表明了線程要完成的任務。所以把run()方法稱爲線程執行體。
  • 建立Thread子類的實例,即建立線程對象。
  • 調用線程對象的start()方法來啓動線程。
public class ThreadTest extends Thread{

    private int i;
    public void run() {
        for(i=0;i<50;i++) {
            //當線程繼承Thread類的時候,直接調用this便可獲取當前的進程
            //Thread類的getName()方法會返回線程的名字
            System.out.println(getName()+" "+i);
        }
    }
    public static void main(String[] args) {
        for(int i =0;i<50;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==10) {
                //建立第一個線程
                new ThreadTest().start();
                //建立第二個線程
                new ThreadTest().start();
            }
        }
    }
}

2.2 Runnable接口

  實現Runnable接口建立線程類ide

  • 定義Runnable接口的實現類,並重寫run()方法。
  • 建立Runnable實現類的實例,並以此實例做爲Thread的target來建立Thread方法。
  • 調用線程的start()方法來啓動對象。
public class RunnableTest implements Runnable {
    private int i;
    @Override
    public void run() {
        for(i=0;i<50;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }

    public static void main(String[] args) {
        for(int i =0;i<50;i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
            if(i==10) {
                RunnableTest rs = new RunnableTest();
                new Thread(rs).start();
                new Thread(rs).start();
            }
        }
    }
}

  實現Runnable的原理和好處函數

  • 程序設計遵循的原則:開閉原則,對修改關閉,對擴展開放,減小線程自己和任務之間的耦合性。
  • 實現Runnable接口避免了單繼承的侷限性,因此較爲經常使用。實現Runnable接口的方式,更加的符合面向對象,線程分爲兩部分,一部分線程對象,一部分線程任務。
  • 繼承Thread類,線程對象和線程任務耦合在一塊兒。一旦建立Thread類的子類對象,既是線程對象,又有線程任務。
  • 實現runnable接口,將線程任務單獨分離出來封裝成對象,類型就是Runnable接口類型。Runnable接口對線程對象和線程任務進行解耦。

2.3 匿名內部類

  使用線程的內匿名內部類方式,能夠方便的實現每一個線程執行不一樣的線程任務操做。this

  • 方式1:建立線程對象時,直接重寫Thread類中的run方法
new Thread() {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + " " + i);
        }
    }
}.start();
  • 方式2:使用匿名內部類的方式實現Runnable接口,從新Runnable接口中的run方法
new Thread(new Runnable() {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
}).start();

2.4 多線程經常使用方法

  • 獲取名字
    經過getName()方法獲取線程對象的名字
  • 設置名字
    • 經過構造函數能夠傳入String類型的名字
    new Thread("yyy") {
        public void run() {
            //do something...
        }
    }.start();
    • 經過setName(String)方法能夠設置線程對象的名字
    new Thread() {
        public void run() {
            this.setName("abc");
            for(int i = 0; i < 100; i++) {
                System.out.println(this.getName() + "....bb");
            }
        }
    }.start();
  • 獲取當前線程對象
    經過Thread.currentThread(), 主線程也能夠獲取
  • 休眠線程
    Thread.sleep(毫秒,納秒), 控制當前線程休眠若干毫秒
  • 守護線程
    setDaemon(), 設置一個線程爲守護線程, 該線程不會單獨執行, 當其餘非守護線程都執行結束後, 自動退出
Thread t1 = new Thread() {
    public void run() {
        for(int i = 0; i < 50; i++) {
            System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
};
Thread t2 = new Thread() {
    public void run() {
        for(int i = 0; i < 2; i++) {
            System.out.println(getName() + "...bb");
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
};

t1.setDaemon(true);                     //將t1設置爲守護線程

t1.start();
t2.start();
//運行結果
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
Thread-1...bb
Thread-0...aaaaaaaaaaaaaaaaaaaaaa
  • 加入線程
    • join(), 當前線程暫停, 等待指定的線程執行結束後, 當前線程再繼續
    • join(int), 能夠等待指定的毫秒以後繼續
    final Thread t1 = new Thread() {
        public void run() {
            for(int i = 0; i < 100; i++) {
                System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
    
            }
        }
    };
    Thread t2 = new Thread() {
        public void run() {
            for(int i = 0; i < 100; i++) {
                System.out.println(getName() + "...bb");
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
            }
        }
    };
    t1.start();
    t2.start();

    2.5 同步代碼塊和同步方法

  • 同步代碼塊
      當多線程併發,有多段代碼同時執行時,咱們但願某一段代碼執行的過程當中CPU不要切換到其餘線程工做,這時就須要同步。使用synchronized關鍵字加上一個鎖對象來定義一段代碼, 這就叫同步代碼塊。多個同步代碼塊若是使用相同的鎖對象,那麼他們就是同步的。若是兩段代碼是同步的, 那麼同一時間只能執行一段, 在一段代碼沒執行結束以前, 不會執行另一段代碼。
public class ThreadT {

    public void print1() {
        synchronized (ThreadT.class) {
            System.out.print("1");
            System.out.print("2");
            System.out.print("3");
            System.out.print("\r\n");
        }

    }

    public void print2() {
        synchronized (ThreadT.class) {
            System.out.print("a");
            System.out.print("b");
            System.out.print("c");
            System.out.print("\r\n");
        }

    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print2();
                }
            }
        };
        t1.start();
        t2.start();
    }
}
  • 同步方法
      使用synchronized關鍵字修飾一個方法, 該方法中全部的代碼都是同步的。
public class ThreadT {

    public synchronized void print1() {
        System.out.print(Thread.currentThread().getName()+"----");
        System.out.print("1");
        System.out.print("2");
        System.out.print("3");
        System.out.print("\r\n");
    }

    public synchronized void print2() {
        System.out.print(Thread.currentThread().getName()+"----");
        System.out.print("a");
        System.out.print("b");
        System.out.print("c");
        System.out.print("\r\n");
    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    tT.print2();
                }
            }
        };
        t1.start();
        t2.start();
    }
}
//輸出結果(一部分)
Thread-0----123
Thread-0----123
Thread-1----abc
Thread-1----abc
Thread-1----abc
Thread-0----123
Thread-0----123

2.6 線程間通訊

  多個線程併發執行時,在默認狀況下CPU是隨機切換線程的。若是咱們但願他們有規律的執行,就可使用通訊,例如每一個線程執行一次打印。
  若是但願線程等待,就調用wait(),若是但願喚醒等待的線程,就調用notify();這兩個方法必須在同步代碼中執行,而且使用同步鎖對象來調用。notifyAll()方法是喚醒全部線程,JDK5以前沒法喚醒指定的一個線程,若是多個線程之間通訊,須要使用notifyAll()通知全部線程,用while來反覆判斷條件。線程

public class ThreadT {
    
    private int flag = 1;

    public synchronized void print1() {
        try {
            /*
            if (flag != 1) {    //if 語句是在那裏等待就在那裏起來
                this.wait();
            }*/
            while(flag != 1){
                this.wait();    //while循環是循環判斷,每次都會判斷標記
            }
            System.out.print("1");
            System.out.print("2");
            System.out.print("3");
            System.out.print("\r\n");
            flag = 2;
            //this.notify();
            this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void print2() {
        try {
            while(flag != 2){
                this.wait();  
            }
            System.out.print("a");
            System.out.print("b");
            System.out.print("c");
            System.out.print("\r\n");
            flag = 3;
            this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized void print3() {
        try {
            while(flag != 3){
                this.wait();  
            }
            System.out.print("+");
            System.out.print("-");
            System.out.print("*");
            System.out.print("\r\n");
            flag = 1;
            this.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print2();
                }
            }
        };
        Thread t3 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print3();
                }
            }
        };
        t1.start();
        t2.start();
        t3.start();
    }
}
//輸出結果(一部分)
......
abc
+-*
123
abc
+-*
123
......

2.7 互斥鎖

  使用ReentrantLock類的lock()和unlock()方法進行同步。使用Condition的await()和signal()來暫停和喚醒線程設計

public class ThreadT {

    private int flag = 1;
    private ReentrantLock rlock = new ReentrantLock();
    private Condition c1 = rlock.newCondition();
    private Condition c2 = rlock.newCondition();
    private Condition c3 = rlock.newCondition();

    public void print1() {
        rlock.lock();   //得到鎖
        try {
            while (flag != 1) {
                c1.await();  //使當前線程等待,直到發出信號或中斷
            }
            System.out.print("1");
            System.out.print("2");
            System.out.print("3");
            System.out.print("\r\n");
            flag = 2;
            c2.signal();    //喚醒等待線程。 
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rlock.unlock();   //釋放鎖
        }
    }

    public void print2() {
        rlock.lock();
        try {
            while (flag != 2) {
                c2.await();
            }
            System.out.print("a");
            System.out.print("b");
            System.out.print("c");
            System.out.print("\r\n");
            flag = 3;
            c3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rlock.unlock();
        }
    }

    public void print3() {
        rlock.lock();
        try {
            while (flag != 3) {
                c3.await();
            }
            System.out.print("+");
            System.out.print("-");
            System.out.print("*");
            System.out.print("\r\n");
            flag = 1;
            c1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            rlock.unlock();
        }
    }

    public static void main(String[] args) {
        ThreadT tT = new ThreadT();
        Thread t1 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print1();
                }
            }
        };
        Thread t2 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print2();
                }
            }
        };
        Thread t3 = new Thread() {
            public void run() {
                for (int i = 0; i < 100; i++) {
                    tT.print3();
                }
            }
        };
        t1.start();
        t2.start();
        t3.start();
    }
}
相關文章
相關標籤/搜索