多線程二 synchronized的使用

1.synchronized 鎖有兩種:

    1.類的實例

    2.類對象

第一種類對象

    public static void test(){
        System.out.println(Thread.currentThread().getName()+" start ");
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" end ");
    }

    public void test2(){
        synchronized (getClass()) {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" run ");
        }
    }

    public static void main(String[] args) {
        TestThread10 t = new TestThread10();
        new Thread(()->TestThread10.test(),"線程 1 ").start();
        new Thread(()->t.test2(),"線程 2 ").start();
    }

其結果如下:

在線程1,也就是先啓動線程1且等線程1走完,才執行線程2

第二種類的實例

那就是兩個不同的鎖,不會干擾

public void test2(){
    synchronized (this) {
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" run ");
    }
}

2.使用synchronized的時候,出現異常一定要處理,不然他會自動釋放鎖

它的機制是手動加鎖,自動釋放鎖。下面看一個例子,在異常的地方一定要處理異常,不然就會想下面代碼中的線程1,會被釋放掉。

private Integer c = 0;

@Override
public void run() {
  count();
}

private synchronized void count(){
    System.out.println(Thread.currentThread().getName()+" start。。。");
    while (true) {
        System.out.println(Thread.currentThread().getName()+" count="+c++);
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (c == 5) {
            int i = 1/0;
        }
    }

}

public static void main(String[] args) {
    TestThread3 t = new TestThread3();
    Thread t1 = new Thread(t, "線程 1");
    Thread t2 = new Thread(t, "線程 2");
    t1.start();
    t2.start();
}

3.主線程和子線程

線程分用戶線程和守護線程,main方法其實是一個主線程,在操作系統啓動java.exe後,是開啓了一個進程,然後進程啓動main線程,main線程有啓動其他線程。

但是子線程不受主線程影響,當主線程結束後,其子線程任然在運行,就像上面代碼所執行的結果一樣,主線程啓動完子線程後就結束了,但啓動的兩個子線程都是非守護線程,即不受主線程影響;所以當子線程1異常結束,線程2任然繼續

設置守護線程,記住,設置守護線程要在start方法之前設置

t2.setDaemon(true);
t2.start();

這裏有兩條原則:

  • 主線程結束,用戶線程繼續執行,

  • 所有子線程都是守護線程,那麼主線程結束,所有子線程都會結束,如果存在用戶線程,那麼在用戶線程結束後結束

4.volatile的作用

  • 防止計算機指令的重排序

  • 保證線程間變量的可見性

它不保證原子性,是針對java而實現的功能

看下面代碼,對同一個對象的變量進行自增,結果是100000,貌似很正常

public class TestThread4{
    private int c = 0;

    private void count(){
        for (int i = 0; i < 100000; i++) {
            System.out.println("count = "+ ++c);;
        }
    }

    public static void main(String[] args) {
        TestThread4 t = new TestThread4();
        List<Thread> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(new Thread(t::count,"thread-"+i));
        }

        list.forEach(a->a.start());
        list.forEach(a-> {
            try {
                a.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("main end");
    }
}

我們在c上增加volatile修飾:

private volatile int c = 0;

比如在第一個線程在拿到c後進行自增,同時另一個線程也去拿了c,都同時自增,然後都寫入同樣的值,導致的這樣的結果。

5.notify是隨機啓動等待線程中的一個,並且跟線程優先級無關

notify是隨機啓動等待線程中的一個,並且跟線程優先級無關,且 wait和notify方法要在同一把lock的情況下使用;還有一點是lock.wait 阻塞還後會把鎖讓出給需要的線程,然而,在其他線程執行完後,調用lock.notify(),喚醒等待的線程,但是在當前鎖裏的代碼沒執行完,不會釋放掉鎖。

簡單場景模擬:

一個固定容量同步容器,擁有put和get方法,以及getCount方法,能夠支持兩個生產者線程以及10個消費者線程的阻塞調用。

public class TestThread8 {

    private final LinkedList list = new LinkedList();

    private final int MAX = 10;

    private int count = 0;

    public synchronized void put(Object o) {
        while (list.size() == MAX) {
            try {
                // 在這裏等待;的那個調用notify時會從這裏繼續執行
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.add(o);
        count++;
        // 啓動所有線程,包括生產者,隨機的
        this.notifyAll();
    }

    public synchronized void get() {
        while (list.size() == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.removeLast();
        count--;
        this.notifyAll();
    }

    public int getCount() {
        return list.size();
    }

    public static void main(String[] args) {
        TestThread8 t = new TestThread8();

        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                int j = 0;
                while (true) {
                    t.put(Thread.currentThread().getName() + " put " + t.getCount());
                    System.out.println(Thread.currentThread().getName() + " put " + t.getCount());
                }
            }).start();
        }

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    t.get();
                    System.out.println(Thread.currentThread().getName() + " get " + t.getCount());
                }
            }).start();
        }
    }
}

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章