日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java安全编码指南之:lock和同步的正确使用

發布時間:2024/2/28 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java安全编码指南之:lock和同步的正确使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 簡介
  • 使用private final object來作為lock對象
  • 不要synchronize可被重用的對象
  • 不要sync Object.getClass()
  • 不要sync高級并發對象
  • 不要使用Instance lock來保護static數據
  • 在持有lock期間,不要做耗時操作
  • 正確釋放鎖

簡介

在java多線程環境中,lock和同步是我們一定會使用到的功能。那么在java中編寫lock和同步相關的代碼之后,需要注意哪些問題呢?一起來看看吧。

使用private final object來作為lock對象

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

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

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

解決這種問題的方法就是使用private final object來作為lock的對象。因為是private的,所以惡意對象無法獲取到該對象的鎖,從而避免了問題的產生。

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

所以,我們推薦使用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 foreverThread.sleep(10000);}}} }

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

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

我們看第二種寫法:

public Object lock = new Object();public void doSomething2(){synchronized (lock){//do something}}

上面的例子中,我們同步了一個public對象,但是因為該對象是public的,所以惡意程序完全可以訪問該public字段,并且永久獲得這個對象的monitor,從而產生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的方法來對該對象進行修改。所以也是有安全問題的。

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

private final Object lock4= new Object();public void doSomething4() {synchronized (lock4) {// do something}}

我們再考慮一下靜態方法的情況:

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

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

不要synchronize可被重用的對象

之前我們在講表達式規則的時候,提到了封裝類對象的構建原則:

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

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

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

舉個例子:

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

上面從基礎類型構建的Boolean對象其實是同一個對象。

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

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

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

同樣的問題也可能出現在String中:

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

因為String對象有字符串常量池,直接通過字符串來創建的String對象其實是同一個對象。所以上面的代碼是有安全問題的。

解決辦法就是使用new來新創建一個對象。

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

不要sync Object.getClass()

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

看下面的一個例子:

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方法實際上獲得了兩個鎖,一個是SycClass,一個是SycClassSub,從而產生了安全隱患。

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

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

我們可以直接調用SycClass.class也可以使用Class.forName來獲取。

不要sync高級并發對象

我們把實現了java.util.concurrent.locks包中的Lock和Condition接口的對象稱作高級并發對象。比如:ReentrantLock。

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

public class SyncLock {private final Lock lock = new ReentrantLock();public void doSomething(){synchronized (lock){//do something}} }

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

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

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

不要使用Instance lock來保護static數據

一個class中可以有static類變量,也可以有實例變量。類變量是和class相關的,而實例變量是和class的實例對象相關的。

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

看下面的一個例子:

public class SyncStatic {private static volatile int age;public synchronized void doSomething(){age++;} }

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

但是上面的例子中,我們使用了synchronized關鍵字,同步的實際上是SyncStatic的實例對象,如果有多個線程創建多個實例對象同時調用doSomething方法,完全是可以并行進行的。從而導致++操作出現問題。

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

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++;}}

在持有lock期間,不要做耗時操作

如果在持有lock期間,我們進行了比較耗時的操作,像I/O操作,那么持有lock的時間就會過長,如果是在高并發的情況下,就有可能出現線程餓死的情況,或者DOS。

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

正確釋放鎖

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

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

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

本文的代碼:

learn-java-base-9-to-20/tree/master/security

本文已收錄于 http://www.flydean.com/java-security-code-line-lock/

最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程序那些事」,懂技術,更懂你!

總結

以上是生活随笔為你收集整理的java安全编码指南之:lock和同步的正确使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。