1. 程式人生 > 其它 >java中關於執行緒Thread的建立和同步總結

java中關於執行緒Thread的建立和同步總結

技術標籤:java

一、程序與執行緒的概念

程序是記憶體中執行的一個應用程式,執行緒是程序中的一個執行單元。
一個程式可以有多個程序,一個程序可以有多個執行緒且至少有一個執行緒。

二、Java中建立執行緒的兩種方式

  1. 定義Thread的子類,並重寫該類的run方法
public class MyThread extends Thread{

   @Override
    public void run() {
        for (int i=0;i<20;i++){
            System.out.println("齊天大聖:"+i);
        }
} } public class HelloThread { public static void main(String[] args) { // 建立子類例項,即建立了執行緒物件 MyThread myThread = new MyThread(); myThread.start();// 呼叫start()方法啟動該執行緒 } }
  1. 定義Runnable介面的實現類,並重寫該介面run()方法
public class MyRunnable implements Runnable{
    @Override
public void run() { for (int i=0;i<10;i++){ // 輸出內容:執行緒名稱:i System.out.println(Thread.currentThread().getName()+":"+i); } } } public class HelloThread { public static void main(String[] args) { // 建立該介面實現類的例項 MyRunnable mr = new MyRunnable
(); // 以此例項mr 作為Thread的target來建立Thread類的物件, // 注意!該物件tr才是真正的執行緒物件(其實這種建立方式也就實現了同一執行緒的資源共享!) Thread tr= new Thread(mr,"執行緒1"); tr.start(); } }

三、兩種建立執行緒的方式Thread和Runnable的區別

若一個類繼承Thread,則不適合資源共享。若實現了Runnable,則很容易資源共享。(資源共享簡單舉例就是多個視窗賣100張票那個例子)
總結: 實現Runnable介面比繼承Thread類所具有的優勢:

  1. 適合多個相同的程式程式碼的執行緒去共享同一個資源。
  2. 可以避免java中的單繼承的侷限性。
  3. 增加程式的健壯性,實現解耦操作,程式碼可以被多個執行緒共享,程式碼和執行緒獨立。
  4. 執行緒池只能放入實現Runable或Callable類執行緒,不能直接放入繼承Thread的類。
    (其實,不管是繼承Thread類還是實現Runnable介面來建立執行緒,最終都是通過Thread的物件API來控制執行緒的。)

四、執行緒安全

這裡舉最簡單的賣票案例來說明
首先定義一個實現Runnable介面的類Ticket

public class Ticket implements Runnable{

    private int tickets=100;
	
	@Override
    public void run() {
        while (true){
            
                if (tickets>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+" 正在賣第:"+tickets--+"張票");
                }
           
        }
    }
}

建立測試類,開始賣票

public class TestDemo {

    public static void main(String[] args) {
		// 建立執行緒任務物件
        Ticket ticket = new Ticket();
		
		// 建立3個視窗物件(Runnable的好處在此就體現出來了,3個執行緒可以同時共享這100張票,實現了資源共享)
        Thread t1 = new Thread(ticket,"視窗1");
        Thread t2 = new Thread(ticket,"視窗2");
        Thread t3 = new Thread(ticket,"視窗3");
		
		// 3個視窗開始賣票
        t1.start();
        t2.start();
        t3.start();

    }
}

輸入結果:
在這裡插入圖片描述
此時出現了執行緒安全的問題,5出現2次,且票數出現了0和-1
這是因為當執行緒1在執行時,還沒執行完,執行緒2或者3也執行開始,導致票數在不同的執行緒裡出現了重複或者不存在。
解決方案:

  1. 利用java中提供的同步機制synchronized關鍵字來解決
public class Ticket implements Runnable{

    private int tickets=100;

    private  Object lock = new Object();
	
	@Override
    public void run() {
        while (true){
           
            synchronized (lock){ // lock 是一個鎖物件,該物件可以是任意型別,但是多個物件要使用同一把鎖才能起到效果
                if (tickets>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+" 正在賣第:"+tickets--+"張票");
                }
            }

        }
    }
}

2.Lock鎖:java.util.concurrent.locks.Lock 機制提供了比synchronized程式碼塊和synchronized方法更廣泛的鎖定操作, 同步程式碼塊/同步方法具有的功能Lock都有,除此之外更強大,更體現面向物件。

public class Ticket implements Runnable{

    private int tickets=100;

    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            lock.lock(); // 加同步鎖
            
           if (tickets>0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName()+" 正在賣第:"+tickets--+"張票");
            }
          

            lock.unlock(); // 釋放同步鎖

        }
    }
}