java線程基礎知識----線程與鎖


  我們上一章已經談到java線程的基礎知識,我們學習了Thread的基礎知識,今天我們開始學習java線程和鎖。

  1.  首先我們應該了解一下Object類的一些性質以其方法,首先我們知道Object類的是java的頂層類,所有的類都集成自Object類,包括string和數組。而且每一個Object都有一個鎖,同一時間只能有一個線程暫用這個對象的鎖。這是我們今天學習的前提條件,至於Object的一些方法我們在后面的章節中會進行學習。

  2.  java鎖之synchronized: 想必大家都知道java的synchronized關鍵字,在我看來這是鎖操作中相對簡單的方法,但是對事物我總有一個定義“簡單的就是可擴展性差的”,下面我們將了解synchronized關鍵字的用法:

A. 入門基礎實例: 舉一個經典的售票例子,就是同時有2個窗口售票(當然是電子票),而且票的數量是一定的,假設20張。兩個窗口之間售票相互獨立。我們應該怎么實現?

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException{
        Thread1 thread1 = new Thread1();
        Thread threadA = new Thread(thread1);
        Thread threadB = new Thread(thread1);
        threadA.start();
        threadB.start();
    }
}
class Thread1 implements Runnable{
    private Integer ticket = 100;
    public void run(){
            while ((ticket--) >0) {
                System.out.println("窗口"+Thread.currentThread().getName()+"賣票,當前票的數量為"+ticket);
            }
    }
}
計算結果: 具有隨機性和不唯一性

  顯然這樣的結果並不是我們想要的,這里我們先分析一下原因,為什么會導致這種計算結果?在訪問臨界區資源的時候我們並沒有控制進入臨界區的線程數量,也就是說我們在這個過程中可能有兩個線程同時進入了這段"臨界區",訪問了"臨界資源"。假設有兩個線程同時訪問到了ticket值為100,但是兩個線程都進行了--操作,導致本來線程應該為98的,但是實際結果確實99。這其實就是線程不安全的。

  那么我們如何更改上面的程序進行更改呢?我們只需要引入synchronized關鍵字,首先我們說一下synchronized方法的兩種使用方式:

    一: synchronized代碼塊:如上面的程序只需要進行如下更改即可:

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException{
        Thread1 thread1 = new Thread1();
        Thread threadA = new Thread(thread1);
        Thread threadB = new Thread(thread1);
        threadA.start();
        threadB.start();
    }
}
class Thread1 implements Runnable{
    private Integer ticket = 100;
    public void run(){
        synchronized (ticket) {
            while ((ticket--) >0) {
                System.out.println("窗口"+Thread.currentThread().getName()+"賣票,當前票的數量為"+ticket);
            }
        }
    }
}
計算結果:  這樣就能夠按照我們預定的順序進行程序輸出了。

但是我們需要注意以下問題:  第一:我們設置了synchronized代碼塊,那么想要訪問這段代碼塊的線程就會被阻塞。

  第二: 同樣我們應該注意一個問題,我們獲取到的鎖是對象鎖,那么如果在Thread類中存在多個synchronized方法,並且我們獲取的對象的鎖就是Thread類,那么只要有一個線程獲取到了對象鎖,那么另外的synchronized方法講會被阻塞。

二: synchronized方法:  與上面類似,語法如下: public void synchronized test();

 3. Object類中與線程鎖相關的方法:

A.  wait方法: 首先我們知道wait方法使當前線程釋放對象鎖,讓其他線程可以獲取對象鎖進入同步代碼塊。並且將當前線程放入到對象等待池。

B.       notify與notifyAll方法: 在當前線程中調用notify方法,則可以喚醒等待對象鎖的線程執行。但是需要注意一點,如果對象等待池中存在很多線程,我們調用notifyAll方法的話,選擇線程是隨機的。

  我們在使用的時候還應該注意一個問題,使用這些方法都必須在獲取對象鎖的代碼塊或者方法中使用。

  綜合例子:

public class ThreadTest {
    public static Integer temp = 0;
    public static void main(String[] args) throws InterruptedException{
        ThreadA threadA = new ThreadA();
        ThreadB threadB =new ThreadB();
        threadA.start();
        threadB.start();
    }
}
class ThreadA extends Thread{
    public void run() {
        synchronized (ThreadTest.temp) {
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+"輸出"+i);
                if(i== 3)
                    try {
                        System.out.println("線程調用wait方法");
                        ThreadTest.temp.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
            }
        }
    }
}
class ThreadB extends Thread{
    public void run() {
        synchronized (ThreadTest.temp) {
            for(int i = 0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+"輸出"+i);
                if(i==4){
                    System.out.println(Thread.currentThread().getName()+"結束");
                    System.out.println("線程調用notify方法");
                    ThreadTest.temp.notify();
                }
            }
        }
    }
}
輸出結果:

Thread-0輸出0
Thread-0輸出1
Thread-0輸出2
Thread-0輸出3
線程調用wait方法
Thread-1輸出0
Thread-1輸出1
Thread-1輸出2
Thread-1輸出3
Thread-1輸出4
Thread-1結束
線程調用notify方法
Thread-0輸出4

  


免責聲明!

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



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