java安全編碼指南之如何正確使用lock和同步

這篇文章主要講解了“java安全編碼指南之如何正確使用lock和同步”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“java安全編碼指南之如何正確使用lock和同步”吧!

創(chuàng)新互聯(lián)公司專注于新?lián)峋W(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠為您提供新?lián)釥I銷型網(wǎng)站建設(shè),新?lián)峋W(wǎng)站制作、新?lián)峋W(wǎng)頁設(shè)計(jì)、新?lián)峋W(wǎng)站官網(wǎng)定制、微信小程序定制開發(fā)服務(wù),打造新?lián)峋W(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供新?lián)峋W(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

使用private final object來作為lock對象

一般來說我們在做多線程共享對象的時候就需要進(jìn)行同步。java中有兩種同步方式,第一種就是方法同步,第二種是同步塊。

如果我們在實(shí)例方法中使用的是synchronized關(guān)鍵字,或者在同步塊中使用的是synchronized(this),那么會以該該對象的實(shí)例作為monitor,我們稱之為intrinsic lock。

如果有惡意代碼惡意獲取該對象的鎖并且釋放,那么我們的系統(tǒng)將不能及時響應(yīng)正常的服務(wù),將會遭受到DOS攻擊。

解決這種問題的方法就是使用private final object來作為lock的對象。因?yàn)槭莗rivate的,所以惡意對象無法獲取到該對象的鎖,從而避免了問題的產(chǎn)生。

如果是在類方法(static)中使用了synchronized關(guān)鍵字,那么將會以這個class對象作為monitor。這種情況下,惡意對象可以通過該class的子類或者直接獲取到該class,然后通過調(diào)用getClass()獲取到class對象,從而進(jìn)行加鎖操作,讓正常服務(wù)無法獲取到鎖。

所以,我們推薦使用private final object來作為lock對象。

下面舉幾個例子來說明:

public class SynObject {

    public synchronized  void doSomething(){
        //do something
    }

    public static void main(String[] args) throws InterruptedException {
        SynObject synObject= new SynObject();
        synchronized (synObject){
            while (true){
                //loop forever
                Thread.sleep(10000);
            }
        }
    }}

上面代碼可能使我們最常使用的代碼,我們在對象中定義了一個synchronized的doSomething方法。

如果有惡意代碼直接拿到了我們要調(diào)用的SynObject對象,并且直接對其進(jìn)行同步,如上例所示,那么這個對象的鎖將永遠(yuǎn)無法釋放。最終導(dǎo)致DOS。

我們看第二種寫法:

 public Object lock = new Object();

    public void doSomething2(){
        synchronized (lock){
            //do something
        }
    }

上面的例子中,我們同步了一個public對象,但是因?yàn)樵搶ο笫莗ublic的,所以惡意程序完全可以訪問該public字段,并且永久獲得這個對象的monitor,從而產(chǎn)生DOS。

再看下面的一個例子:

   private volatile Object lock2 = new Object();

    public void doSomething3() {
        synchronized (lock2) {
            // do something
        }
    }

    public void setLock2(Object lockValue) {
        lock2 = lockValue;
    }

上面的例子中,我們定義了一個private的lock對象,并且使用它來為doSomething3方法加鎖。

雖然是private的,但是我們提供了一個public的方法來對該對象進(jìn)行修改。所以也是有安全問題的。

正確的做法是使用private final Object:

private final Object lock4= new Object();

    public void doSomething4() {
        synchronized (lock4) {
            // do something
        }
    }

我們再考慮一下靜態(tài)方法的情況:

   public static synchronized void doSomething5() {
        // do something
    }synchronized (SynObject.class) {
  while (true) {
    Thread.sleep(10000);
  }}

上面定義了一個public static的方法,從而鎖定的是class對象,惡意代碼可以惡意占有該對象的鎖,從而導(dǎo)致DOS。

不要synchronize可被重用的對象

之前我們在講表達(dá)式規(guī)則的時候,提到了封裝類對象的構(gòu)建原則:

對于Boolean和Byte來說,如果直接從基礎(chǔ)類值構(gòu)建的話,也是同一個對象。

而對于Character來說,如果值的范圍在\u0000 to \u007f,則屬于同一個對象,如果超出了這個范圍,則是不同的對象。

對于Integer和Short來說,如果值的范圍在-128 and 127,則屬于同一個對象,如果超出了這個范圍,則是不同的對象。

舉個例子:

Boolean boolA=true;
Boolean boolB=true;
System.out.println(boolA==boolB);

上面從基礎(chǔ)類型構(gòu)建的Boolean對象其實(shí)是同一個對象。

如果我們在代碼中使用下面的Boolean對象來進(jìn)行同步,則可能會觸發(fā)安全問題:

private final Boolean booleanLock = Boolean.FALSE;public void doSomething() {
  synchronized (booleanLock) {
    // ...
  }}

上面的例子中,我們從Boolean.FALSE構(gòu)建了一個Boolean對象,雖然這個對象是private的,但是惡意代碼可以通過Boolean.FALSE來構(gòu)建一個相同的對象,從而讓private規(guī)則失效。

同樣的問題也可能出現(xiàn)在String中:

private final String lock = "lock";public void doSomething() {
  synchronized (lock) {
    // ...
  }}

因?yàn)镾tring對象有字符串常量池,直接通過字符串來創(chuàng)建的String對象其實(shí)是同一個對象。所以上面的代碼是有安全問題的。

解決辦法就是使用new來新創(chuàng)建一個對象。

private final String lock = new String("LOCK");

不要sync Object.getClass()

有時候我們想要同步class類,Object提供了一個方便的getClass方法來返回當(dāng)前的類。但是如果在父類和子類的情況下,子類的getClass會返回子類的class類而不是父類的class類,從而產(chǎn)生不一致對象同步的情況。

看下面的一個例子:

public class SycClass {

    public void doSomething(){
        synchronized (getClass()){
            //do something
        }
    }}

在SycClass中,我們定義了一個doSomething方法,在該方法中,我們sync的是getClass()返回的對象。

如果SycClass有子類的情況下:

public class SycClassSub extends SycClass{

    public void doSomethingElse(){
        synchronized (SycClass.class){
           doSomething();
        }
    }}

doSomethingElse方法實(shí)際上獲得了兩個鎖,一個是SycClass,一個是SycClassSub,從而產(chǎn)生了安全隱患。

在sync的時候,我們需要明確指定要同步的對象,有兩種方法指定要同步的class:

synchronized (SycClass.class)
synchronized (Class.forName("com.flydean.SycClass"))

我們可以直接調(diào)用SycClass.class也可以使用Class.forName來獲取。

不要sync高級并發(fā)對象

我們把實(shí)現(xiàn)了java.util.concurrent.locks包中的Lock和Condition接口的對象稱作高級并發(fā)對象。比如:ReentrantLock。

這些高級并發(fā)對象看起來也是一個個的Lock,那么我們可不可以直接sync這些高級并發(fā)對象呢?看下面的例子:

public class SyncLock {

    private final Lock lock = new ReentrantLock();

    public void doSomething(){
        synchronized (lock){
            //do something
        }
    }}

看起來好像沒問題,但是我們要注意的是,我們自定義的synchronized (lock)和高級并發(fā)對象中的Lock實(shí)現(xiàn)是不一樣的,如果我們同時使用了synchronized (lock)和Lock自帶的lock.lock(),那么就有可能產(chǎn)生安全隱患。

所以,對于這些高級并發(fā)對象,最好的做法就是不要直接sync,而是使用他們自帶的lock機(jī)制,如下:

  public void doSomething2(){
        lock.lock();
        try{
        //do something
        }finally {
            lock.unlock();
        }
    }

不要使用Instance lock來保護(hù)static數(shù)據(jù)

一個class中可以有static類變量,也可以有實(shí)例變量。類變量是和class相關(guān)的,而實(shí)例變量是和class的實(shí)例對象相關(guān)的。

那么我們在保護(hù)類變量的時候,一定要注意sync的也必須是類變量,如果sync的是實(shí)例變量,就無法達(dá)到保護(hù)的目的。

看下面的一個例子:

public class SyncStatic {
    private static volatile int age;

    public synchronized void doSomething(){
        age++;
    }}

我們定義了一個static變量age,然后在一個方法中希望對其累加。之前的文章我們也講過了,++是一個復(fù)合操作,我們需要對其進(jìn)行數(shù)據(jù)同步。

但是上面的例子中,我們使用了synchronized關(guān)鍵字,同步的實(shí)際上是SyncStatic的實(shí)例對象,如果有多個線程創(chuàng)建多個實(shí)例對象同時調(diào)用doSomething方法,完全是可以并行進(jìn)行的。從而導(dǎo)致++操作出現(xiàn)問題。

同樣的,下面的代碼也是一樣的問題:

   private final Object lock = new Object();
    public  void doSomething2(){
        synchronized (lock) {
            age++;
        }
    }

解決辦法就是定義一個類變量:

   private static final Object lock3 = new Object();
    public  void doSomething3(){
        synchronized (lock3) {
            age++;
        }
    }

在持有l(wèi)ock期間,不要做耗時操作

如果在持有l(wèi)ock期間,我們進(jìn)行了比較耗時的操作,像I/O操作,那么持有l(wèi)ock的時間就會過長,如果是在高并發(fā)的情況下,就有可能出現(xiàn)線程餓死的情況,或者DOS。

所以這種情況我們一定要避免。

正確釋放鎖

在持有鎖之后,一定要注意正確的釋放鎖,即使遇到了異常也不應(yīng)該打斷鎖的釋放。

一般來說鎖放在finally{}中釋放最好:
 

   public void doSomething(){ 
          lock.lock();
                  try{
                          //do something        
                     }
                     finally {
                         lock.unlock();        
                        }    
                      }

感謝各位的閱讀,以上就是“java安全編碼指南之如何正確使用lock和同步”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對java安全編碼指南之如何正確使用lock和同步這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

分享題目:java安全編碼指南之如何正確使用lock和同步
鏈接URL:http://bm7419.com/article30/jjspso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、動態(tài)網(wǎng)站、軟件開發(fā)做網(wǎng)站、網(wǎng)站營銷、靜態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名