Java基礎之並發編程


  前言

  程序:一組有序的指令集合

  進程:執行中的程序

  線程:是進程中“單一持續控制流程”

 

  進程跟程序的區別:程序是一組指令的集合,它是靜態的實體,沒有執行的含義。而進程是一個動態的實體,有自己的生命周期。一般說來,一個進程肯定與一個程序相對應,並且只有 一個,但是一個程序可以有多個進程,或者一個進程都沒有。除此之外,進程還有並發性和交往性。簡單地說,進程是程序的一部分,程序運行的時候會產生進程。

  進程與線程的區別:進程作為資源分配的單位,線程是調度和執行的單位

 

  線程狀態

  新生狀態、就緒狀態、阻塞狀態、運行狀態、死亡狀態

 

 

  多線程的實現

  Thread

  PS:無法實現線程之間的數據共享

    /**
     * 通過 extends Thread
     */
    public class MyThread extends Thread{

        private final String name;

        public MyThread(String name){
            this.name = name;
        }

        @Override
        public void run(){
            for (int i = 0; i < 5; i++) {
                System.out.println(this.name + ":" + i);
            }
        }
    }
    public static void main(String[] args) {
        MyThread myThread = new MyThread("MyThread-0");
        myThread.start();
    }

 

 

  Runnable

    /**
     * 通過 implements Runnable
     */
    public class MyRunnable implements Runnable{

        private final String name;

        public MyRunnable(String name){
            this.name = name;
        }

        @Override
        public void run(){
            for (int i = 0; i < 5; i++) {
                System.out.println(this.name + ":" + i);
            }
        }
    }
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable("MyRunnable-0");
        new Thread(myRunnable).start();

    }

 

 

  Callable<V>

    /**
     * 通過 implements Callable<V>
     */
    public class MyCallable implements Callable<String> {

        private final String name;

        public MyCallable(String name){
            this.name = name;
        }

        @Override
        public String call() throws Exception {
            for (int i = 0; i < 5; i++) {
                System.out.println(this.name + ":" + i);
            }
            return "執行完成";
        }
    }
    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable("MyCallable-0");
        new Thread(new FutureTask<String>(myCallable)).start();

    }

 

 

  線程池

    public static void main(String[] args) {
        MyThread myThread = new MyThread("MyThread-0");
        MyRunnable myRunnable = new MyRunnable("MyRunnable-0");
        MyCallable myCallable = new MyCallable("MyCallable-0");

        //線程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        threadPool.submit(myThread);
        threadPool.submit(myRunnable);
        threadPool.submit(myCallable);

        //關閉線程池
        threadPool.shutdown();
    }

 

 

  並發安全

  synchronized

  使用synchronized關鍵字對線程加鎖,在有線程獲取該內存鎖后,其它線程無法訪問該內存,從而實現JAVA中簡單的同步、互斥操作。

  同步代碼塊

synchronized(Obj){//obj叫做同步監聽器,只能是引用類型

}

  同步方法

public synchronized void fun(){//同步方法中,默認使用this當前對象作為同步對象 

}

 

  Lock

  從jdk1.5之后,java提供了另外一種方式來實現同步訪問,那就是Lock。

//可重入鎖
Lock lock = new ReentrantLock();

//加鎖
lock.lock();

try{
    //並發處理邏輯...
}catch(Exception ex){
     
}finally{
    //釋放鎖
    lock.unlock();
}

 

  老公、老婆同時取錢例子:

/**
 * 銀行賬戶
 */
public class Account {

    //余額
    private int balance = 1000;

    public Account() {
    }

    public Account(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

}

  使用synchronized

/**
 * 通過 implements Runnable
 */
public class MyRunnable implements Runnable{

    private final String name;

    private final Account account;

    public MyRunnable(Account account,String name){
        this.account = account;
        this.name = name;
    }

    @Override
    public void run(){
        for (int i = 0; i < 5; i++) {
            //加鎖,鎖的是銀行賬戶對象
            synchronized (account) {
                //余額大於200
                if (account.getBalance() >= 200) {

                    //模擬耗時
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //取錢成功
                    account.setBalance(account.getBalance() - 200);
                    System.out.println(this.name + "取走了200元,當前余額為:" + account.getBalance());
                } else {
                    System.out.println(this.name + "取錢失敗,余額不足");
                }
            }
        }
    }
}

  使用Lock

/**
 * 通過 implements Runnable
 */
public class MyRunnable implements Runnable{

    private final String name;

    private final Account account;

    //可重入鎖
    private static final Lock lock = new ReentrantLock();

    public MyRunnable(Account account,String name){
        this.account = account;
        this.name = name;
    }

    @Override
    public void run(){
        for (int i = 0; i < 5; i++) {
            //加鎖
            lock.lock();
            
            try{
                //余額大於200
                if (account.getBalance() >= 200) {

                    //模擬耗時
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //取錢成功
                    account.setBalance(account.getBalance() - 200);
                    System.out.println(this.name + "取走了200元,當前余額為:" + account.getBalance());
                } else {
                    System.out.println(this.name + "取錢失敗,余額不足");
                }
            }catch(Exception e){
                e.printStackTrace();
            }finally{
                //釋放鎖
                lock.unlock();
            }
        }
    }
}

  測試

    public static void main(String[] args) {
        //初始化銀行賬戶
        Account account = new Account();

        //老公、老婆同時取錢
        new Thread(new MyRunnable(account,"老公")).start();
        new Thread(new MyRunnable(account,"老婆")).start();
    }

 

 

 

 

  如果不加鎖,就會出現並發安全問題

 

 

 

 

  死鎖

  死鎖是指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程

  死鎖的發生必須具備以下四個必要條件。

  1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。

  2)請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。

  3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。

  4)環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。

 

 

  后記

  Thread.sleep():使線程進入阻塞狀態,會釋放CPU資源但不會釋放對象鎖的控制

  Object.wait():當前線程必須擁有此對象監視器。該線程釋放對此監視器的所有權(釋放synchronize鎖)並等待,直到其他線程通過調用 notify 方法,或 notifyAll 方法通知在此對象的監視器上等待的線程醒來。然后該線程將等到重新獲得對監視器的所有權后才能繼續執行。 

  Thread.sleep()與Object.wait()二者都可以暫停當前線程,釋放CPU控制權,主要的區別在於Object.wait()在釋放CPU同時,釋放了對象鎖的控制


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM