併發編程基礎知識一

線程安全:當多個線程訪問某一個類時,這個類始終都能表現出正確的行爲,那麼這個類就是線程安全的ajax

synchronized: 能夠在任意對象及方法上加鎖,而加鎖的這段代碼稱爲「互斥區」或者「臨界區」安全

private int count = 5;
    
    public synchronized void  run(){
        count--;
        System.err.println(this.currentThread().getName()+" count = "+count);
    }

當多個線程訪問Thread的run()方法時,以排隊的形式進行處理(排隊是按照CPU分配的前後順序而定的),一個線程要執行synchronized 修飾的方法裏面的代碼
1.嘗試得到鎖
2.若是拿到鎖 執行synchronized 代碼塊內容 。若是拿不到鎖 這個線程就會不斷的嘗試得到這把鎖,直到拿到爲止。並且多個線程同時去競爭這把鎖,也就是會有鎖競爭的問題異步


多個線程多個鎖:每一個線程均可以拿到本身的指定的鎖,分別拿到鎖之後執行synchronized方法體裏面的內容。async

synchronized關鍵字取得的鎖都是對象鎖 而不是把一段代碼(方法)當成鎖。
多個線程對象得到不一樣的鎖 他們互不影響。優化

private static int num;
    
    public static  synchronized void printNum(String tag){
        try{
            if(tag.equals("a")){
                num =100;
                System.err.println(" tag a");
                Thread.sleep(2000);
            } else{
                num = 200;
                System.err.println("tag b");
            }
        } catch(Exception e){
            e.printStackTrace();
        }
    }

在靜態方法上面加synchronized 關鍵字 表示鎖定class類,獨佔class類,類級別的鎖this


同步:synchronized 同步的概念就是共享。若是不共享資源,沒有必須進行同步線程

異步:asynchronized 異步就是獨立,相互之間不受制約。例如ajax請求設計

public  synchronized void method1(){
        try {
            System.err.println(Thread.currentThread().getName());
            Thread.sleep(4000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void method2(){
        System.err.println(Thread.currentThread().getName());
    }

1.a線程先持有Object對象的lock鎖 B線程若是在這個時候調用對象中的synchronized 方法則須要等待 ,也就是同步。
2.a線程先持有Object對象的lock鎖 B線程能夠以異步的方式調用線程對象中的非synchronized 修飾的方法 ,不須要等待。日誌


同步的目的是爲了線程安全,對於線程安全須要知足2個特性code

  1. 原子性(同步)
  2. 可見性

髒讀:在設計咱們的程序的時候必定要考慮到問題的總體性,否則就會出現很經典的錯誤:髒讀

public synchronized void setValue(String  username, String password){
            this.username = username;
            this.password = password;
            
            try{
                Thread.sleep(2000);
            }catch(Exception e){
                e.printStackTrace();
            }
            System.err.println("setValue的最終結果是 username "+
            username+",password"+password);
        }
    
    public void getValue(){
        System.err.println("getValue方法獲得username"+
username+",password"+password);
    }

在對一個對象的方法加鎖時候,須要考慮到業務的總體性,即在setValue/getValue方法同時加上synchronized 關鍵字保證業務的原子性


鎖重入:當一個線程得到一個對象的鎖之後 ,再次請求此對象時能夠再次得到對象的鎖

static class Main {
        public int i = 10;
        public synchronized void operationSup(){
            try {
                i--;
                System.out.println("Main print i = " + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    static class Sub extends Main {
        public synchronized void operationSub(){
            try {
                while(i > 0) {
                    i--;
                    System.out.println("Sub print i = " + i);
                    Thread.sleep(100);        
                    this.operationSup();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

當涉及到父類與子類相互執行的時候 可使用鎖重入


異常釋放鎖

private int i = 0;
    
    public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName() +
                 " , i = " + i);
                if(i == 20){
                    //Integer.parseInt("a");
                    throw new RuntimeException();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

若是一個業務分爲多個子模塊去執行。彼此之間相互獨立,若是其中一個業務出現異常,採起的方式是記錄日誌,其餘業務不受影響繼續執行
若是一個業務分爲多個子模塊去執行,彼此之間是關聯的,若是其中一個業務出現異常,採起的方式是拋出RuntimeException,及時終止業務。


減少鎖的粒度:synchronized關鍵字來優化代碼塊的執行時間

public void doLongTimeTask(){
        try {
            
            System.out.println("當前線程開始:" +

 Thread.currentThread().getName() + 
                    ", 正在執行一個較長時間的業務操做,其內容不須要同步");
            Thread.sleep(2000);
            
            synchronized(this){
                System.out.println("當前線程:" + 
                
                Thread.currentThread().getName() + 
                    ", 執行同步代碼塊,對其同步變量進行操做");
                Thread.sleep(1000);
            }
            System.out.println("當前線程結束:" +
 Thread.currentThread().getName() +
                    ", 執行完畢");
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

對任意的Object對象加鎖

public void method1(){
        synchronized (this) {    //對象鎖
            try {
                System.out.println("do method1..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public void method2(){        //類鎖
        synchronized (ObjectLock.class) {
            try {
                System.out.println("do method2..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    private Object lock = new Object();
    public void method3(){        //任何對象鎖
        synchronized (lock) {
            try {
                System.out.println("do method3..");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

對字符串常量加鎖 產生死循環

public void method() {
        //new String("字符串常量")
        synchronized ("字符串常量") {
            try {
                while(true){
                    System.out.println("當前線程 : "  + 
                    Thread.currentThread().getName() + "開始");
                    Thread.sleep(1000);        
                    System.out.println("當前線程 : "  + 
                    Thread.currentThread().getName() + "結束");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

鎖對象改變 若是鎖所對象自己不發生改變,即便是屬性改變,那麼依然是同步的。 同一對象屬性的修改不會影響鎖的狀況

public synchronized void changeAttributte(String name, int age) {
        try {
            System.out.println("當前線程 : "  + 
Thread.currentThread().getName() + " 開始");
            this.setName(name);
            this.setAge(age);
            
            System.out.println("當前線程 : "  + 
Thread.currentThread().getName() + " 修改對象內容爲: " 
                    + this.getName() + ", " + this.getAge());
            
            Thread.sleep(2000);
            System.out.println("當前線程 : "  + 
Thread.currentThread().getName() + " 結束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

死鎖問題:在設計程序時就應該避免雙方相互持有對方的鎖的狀況

public void run() {
        if(tag.equals("a")){
            synchronized (lock1) {
                try {
                    System.out.println("當前線程 : "  + 
                    Thread.currentThread().getName() + " 進入lock1執行");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("當前線程 : "  + 
                    Thread.currentThread().getName() + " 進入lock2執行");
                }
            }
        }
        if(tag.equals("b")){
            synchronized (lock2) {
                try {
                    System.out.println("當前線程 : "  + 
                    Thread.currentThread().getName() + " 進入lock2執行");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("當前線程 : "  + 
                    Thread.currentThread().getName() + " 進入lock1執行");
                }
            }
        }
    }
相關文章
相關標籤/搜索