国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

后端開發(fā)必知的11個(gè)線程安全小技巧

這篇具有很好參考價(jià)值的文章主要介紹了后端開發(fā)必知的11個(gè)線程安全小技巧。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

后端開發(fā)必知的11個(gè)線程安全小技巧

?文章來源地址http://www.zghlxwxcb.cn/news/detail-616945.html

對(duì)于從事后端開發(fā)的同學(xué)來說,線程安全問題是我們每天都需要考慮的問題。

?

線程安全問題通俗地講主要是在多線程的環(huán)境下,不同線程同時(shí)讀和寫公共資源(臨界資源)導(dǎo)致的數(shù)據(jù)異常問題。

?

比如:變量a=0,線程1給該變量+1,線程2也給該變量+1。此時(shí),線程3獲取a的值有可能不是2,而是1。線程3這不就獲取了錯(cuò)誤的數(shù)據(jù)?

?

線程安全問題會(huì)直接導(dǎo)致數(shù)據(jù)異常,從而影響業(yè)務(wù)功能的正常使用,所以這個(gè)問題還是非常嚴(yán)重的。

?

那么,如何解決線程安全問題呢?

?

今天跟大家一起聊聊,保證線程安全的11個(gè)小技巧,希望對(duì)你有所幫助。

?

一、無狀態(tài)

我們都知道只有多個(gè)線程訪問公共資源的時(shí)候,才可能出現(xiàn)數(shù)據(jù)安全問題,那么如果我們沒有公共資源,是不是就沒有這個(gè)問題呢?

?

例如:

public class NoStatusService {


    public void add(String status) {
        System.out.println("add status:" + status);
    }


    public void update(String status) {
        System.out.println("update status:" + status);
    }
}

?

這個(gè)例子中NoStatusService沒有定義公共資源,換句話說是無狀態(tài)的。

?

這種場(chǎng)景中,NoStatusService類肯定是線程安全的。

?

二、不可變

如果多個(gè)線程訪問的公共資源是不可變的,也不會(huì)出現(xiàn)數(shù)據(jù)的安全性問題。

?

例如:


public class NoChangeService {
    public static final String DEFAULT_NAME = "abc";


    public void add(String status) {
        System.out.println(DEFAULT_NAME);
    }
}

?

DEFAULT_NAME被定義成了static final的常量,在多線程中環(huán)境中不會(huì)被修改,所以這種情況也不會(huì)出現(xiàn)線程安全問題。

?

三、無修改權(quán)限

有時(shí)候,我們定義了公共資源,但是該資源只暴露了讀取的權(quán)限,沒有暴露修改的權(quán)限,這樣也是線程安全的。

?

例如:

public class SafePublishService {
    private String name;


    public String getName() {
        return name;
    }


    public void add(String status) {
        System.out.println("add status:" + status);
    }
}

?

這個(gè)例子中,沒有對(duì)外暴露修改name字段的入口,所以不存在線程安全問題。

?

四、synchronized

使用JDK內(nèi)部提供的同步機(jī)制,這也是使用比較多的手段,分為同步方法和同步代碼塊。

?

我們優(yōu)先使用同步代碼塊,因?yàn)橥椒椒ǖ牧6仁钦麄€(gè)方法,范圍太大,相對(duì)來說,更消耗代碼的性能。

?

其實(shí),每個(gè)對(duì)象內(nèi)部都有一把鎖,只有搶到那把鎖的線程,才被允許進(jìn)入對(duì)應(yīng)的代碼塊執(zhí)行相應(yīng)的代碼。

?

當(dāng)代碼塊執(zhí)行完之后,JVM底層會(huì)自動(dòng)釋放那把鎖。

?

例如:

public class SyncService {
    private int age = 1;
    private Object object = new Object();


    //同步方法
    public synchronized void add(int i) {
        age = age + i;        
        System.out.println("age:" + age);
    }


    
    public void update(int i) {
        //同步代碼塊,對(duì)象鎖
        synchronized (object) {
            age = age + i;                     
            System.out.println("age:" + age);
        }    
     }
     
     public void update(int i) {
        //同步代碼塊,類鎖
        synchronized (SyncService.class) {
            age = age + i;                     
            System.out.println("age:" + age);
        }    
     }
}

?

五、Lock

除了使用synchronized關(guān)鍵字實(shí)現(xiàn)同步功能之外,JDK還提供了Lock接口這種顯示鎖的方式。

?

通常我們會(huì)使用Lock接口的實(shí)現(xiàn)類:ReentrantLock,它包含了公平鎖、非公平鎖、可重入鎖、讀寫鎖等更多更強(qiáng)大的功能。

?

例如:

public class LockService {
    private ReentrantLock reentrantLock = new ReentrantLock();
    public int age = 1;
    
    public void add(int i) {
        try {
            reentrantLock.lock();
            age = age + i;           
            System.out.println("age:" + age);
        } finally {
            reentrantLock.unlock();        
        }    
   }
}

?

但如果使用ReentrantLock,它也帶來了一個(gè)小問題,就是需要在finally代碼塊中手動(dòng)釋放鎖。

?

不過說句實(shí)話,使用Lock顯示鎖的方式解決線程安全問題,給開發(fā)人員提供了更多的靈活性。

?

六、分布式鎖

如果是在單機(jī)的情況下,使用synchronized和Lock保證線程安全是沒有問題的。

?

但如果在分布式的環(huán)境中,即某個(gè)應(yīng)用如果部署了多個(gè)節(jié)點(diǎn),每一個(gè)節(jié)點(diǎn)使用可以synchronized和Lock保證線程安全,但不同的節(jié)點(diǎn)之間沒法保證線程安全。

?

這就需要使用分布式鎖了。

?

分布式鎖有很多種,比如:數(shù)據(jù)庫(kù)分布式鎖、zookeeper分布式鎖、redis分布式鎖等。

?

其中我個(gè)人更推薦使用redis分布式鎖,其效率相對(duì)來說更高一些。

?

使用redis分布式鎖的偽代碼如下:

try{
  String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
  if ("OK".equals(result)) {
      return true;
  }
  return false;
} finally {
    unlock(lockKey);
}  

?

同樣需要在finally代碼塊中釋放鎖。

?

七、volatile

有時(shí)候,我們有這樣的需求:如果在多個(gè)線程中,有任意一個(gè)線程,把某個(gè)開關(guān)的狀態(tài)設(shè)置為false,則整個(gè)功能停止。

?

簡(jiǎn)單的需求分析之后發(fā)現(xiàn):只要求多個(gè)線程間的可見性,不要求原子性。

?

如果一個(gè)線程修改了狀態(tài),其他的所有線程都能獲取到最新的狀態(tài)值。

?

這樣一分析這就好辦了,使用volatile就能快速滿足需求。

?

例如:


@Service
public CanalService {
    private volatile boolean running = false;
    private Thread thread;


    @Autowired
    private CanalConnector canalConnector;
    
    public void handle() {
        //連接canal
        while(running) {
           //業(yè)務(wù)處理
        }
    }
    
    public void start() {
       thread = new Thread(this::handle, "name");
       running = true;
       thread.start();
    }
    
    public void stop() {
       if(!running) {
          return;
       }
       running = false;
    }
}

?

需要特別注意的地方是:volatile不能用于計(jì)數(shù)和統(tǒng)計(jì)等業(yè)務(wù)場(chǎng)景。因?yàn)関olatile不能保證操作的原子性,可能會(huì)導(dǎo)致數(shù)據(jù)異常。

?

八、ThreadLocal

除了上面幾種解決思路之外,JDK還提供了另外一種用空間換時(shí)間的新思路:ThreadLocal。

?

當(dāng)然ThreadLocal并不能完全取代鎖,特別是在一些秒殺更新庫(kù)存中,必須使用鎖。

?

ThreadLocal的核心思想是共享變量在每個(gè)線程都有一個(gè)副本,每個(gè)線程操作的都是自己的副本,對(duì)另外的線程沒有影響。

?

溫馨提醒一下:我們平常在使用ThreadLocal時(shí),如果使用完之后,一定要記得在finally代碼塊中,調(diào)用它的remove方法清空數(shù)據(jù),不然可能會(huì)出現(xiàn)內(nèi)存泄露問題。

?

例如:

public class ThreadLocalService {
    private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();


    public void add(int i) {
        Integer integer = threadLocal.get();
        threadLocal.set(integer == null ? 0 : integer + i);
    }
}

?

九、線程安全集合

有時(shí)候,我們需要使用的公共資源放在某個(gè)集合當(dāng)中,比如:ArrayList、HashMap、HashSet等。

?

如果在多線程環(huán)境中,有線程往這些集合中寫數(shù)據(jù),另外的線程從集合中讀數(shù)據(jù),就可能會(huì)出現(xiàn)線程安全問題。

?

為了解決集合的線程安全問題,JDK專門給我們提供了能夠保證線程安全的集合。

?

比如:CopyOnWriteArrayList、ConcurrentHashMap、CopyOnWriteArraySet、ArrayBlockingQueue等。

?

例如:

public class HashMapTest {


    private static ConcurrentHashMap<String, Object> hashMap = new ConcurrentHashMap<>();


    public static void main(String[] args) {


        new Thread(new Runnable() {
            @Override
            public void run() {
                hashMap.put("key1", "value1");
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                hashMap.put("key2", "value2");
            }
        }).start();


        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(hashMap);
    }
}

?

在JDK底層,或者spring框架當(dāng)中,使用ConcurrentHashMap保存加載配置參數(shù)的場(chǎng)景非常多。

?

比較出名的是spring的refresh方法中,會(huì)讀取配置文件,把配置放到很多的ConcurrentHashMap緩存起來。

?

十、CAS

JDK除了使用鎖的機(jī)制解決多線程情況下數(shù)據(jù)安全問題之外,還提供了CAS機(jī)制。

?

這種機(jī)制是使用CPU中比較和交換指令的原子性,JDK里面是通過Unsafe類實(shí)現(xiàn)的。

?

CAS內(nèi)部包含了四個(gè)值:舊數(shù)據(jù)、期望數(shù)據(jù)、新數(shù)據(jù)和地址,比較舊數(shù)據(jù)和期望的數(shù)據(jù),如果一樣的話,就把舊數(shù)據(jù)改成新數(shù)據(jù)。如果不一樣的話,當(dāng)前線程不斷自旋,一直到成功為止。

?

不過,使用CAS保證線程安全,可能會(huì)出現(xiàn)ABA問題,需要使用AtomicStampedReference增加版本號(hào)解決。

?

其實(shí),實(shí)際工作中很少直接使用Unsafe類的,一般用atomic包下面的類即可。

public class AtomicService {
    private AtomicInteger atomicInteger = new AtomicInteger();
    
    public int add(int i) {
        return atomicInteger.getAndAdd(i);
    }
}

?

十一、數(shù)據(jù)隔離

?

有時(shí)候,我們?cè)诓僮骷蠑?shù)據(jù)時(shí),可以通過數(shù)據(jù)隔離,來保證線程安全。

?

例如:

?


public class ThreadPoolTest {


    public static void main(String[] args) {


      ExecutorService threadPool = new ThreadPoolExecutor(8, //corePoolSize線程池中核心線程數(shù)
      10, //maximumPoolSize 線程池中最大線程數(shù)
      60, //線程池中線程的最大空閑時(shí)間,超過這個(gè)時(shí)間空閑線程將被回收
      TimeUnit.SECONDS,//時(shí)間單位
      new ArrayBlockingQueue(500), //隊(duì)列
      new ThreadPoolExecutor.CallerRunsPolicy()); //拒絕策略


      List<User> userList = Lists.newArrayList(
      new User(1L, "蘇三", 18, "成都"),
      new User(2L, "蘇三說技術(shù)", 20, "四川"),
      new User(3L, "技術(shù)", 25, "云南"));


      for (User user : userList) {
          threadPool.submit(new Work(user));
      }


      try {
          Thread.sleep(100);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(userList);
  }


    static class Work implements Runnable {
        private User user;


        public Work(User user) {
            this.user = user;
        }


        @Override
        public void run() {
            user.setName(user.getName() + "測(cè)試");
        }
    }
}

?

這個(gè)例子中,使用線程池處理用戶信息。

?

每個(gè)用戶只被線程池中的一個(gè)線程處理,不存在多個(gè)線程同時(shí)處理一個(gè)用戶的情況。所以這種人為的數(shù)據(jù)隔離機(jī)制,也能保證線程安全。

?

數(shù)據(jù)隔離還有另外一種場(chǎng)景:kafka生產(chǎn)者把同一個(gè)訂單的消息,發(fā)送到同一個(gè)partion中。每一個(gè)partion都部署一個(gè)消費(fèi)者,在kafka消費(fèi)者中,使用單線程接收消息,并且做業(yè)務(wù)處理。

?

這種場(chǎng)景下,從整體上看,不同的partion是用多線程處理數(shù)據(jù)的,但同一個(gè)partion則是用單線程處理的,所以也能解決線程安全問題。

?

作者丨蘇三呀

到了這里,關(guān)于后端開發(fā)必知的11個(gè)線程安全小技巧的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 測(cè)試人員必知的軟件測(cè)試文檔有哪些?

    測(cè)試人員必知的軟件測(cè)試文檔有哪些? 軟件測(cè)試文檔一般是提供測(cè)試信息的一組文檔,可以是測(cè)試人員的工具,也可以是項(xiàng)目開發(fā)團(tuán)隊(duì)的開發(fā)輔助工具。 一般情況下,項(xiàng)目相關(guān)的測(cè)試文檔主要有以下幾個(gè) : 1.測(cè)試計(jì)劃 。測(cè)試計(jì)劃由測(cè)試小組編寫完成后,需同項(xiàng)目中相關(guān)人員

    2024年02月04日
    瀏覽(22)
  • 網(wǎng)絡(luò)管理員必知的域(DOmain)知識(shí):詳解部署與操作

    網(wǎng)絡(luò)管理員必知的域(DOmain)知識(shí):詳解部署與操作

    數(shù)據(jù)來源 ?下面會(huì)涉及一些IP地址和DNS服務(wù)器的一些基礎(chǔ)知識(shí)不熟悉的可以先看這篇文章:IP地址詳解? DNS部署與安全 ????????1) 工作組:默認(rèn)模式,人人平等,不不方便管理(我和你的電腦是屬于平等的,我很難直接控制的你電腦,除非你允許) ????????2) 域:人

    2024年02月03日
    瀏覽(21)
  • 企業(yè)級(jí)項(xiàng)目開發(fā)中保證接口安全的11個(gè)小技巧,詳細(xì)案例指導(dǎo)

    企業(yè)級(jí)項(xiàng)目開發(fā)中保證接口安全的11個(gè)小技巧,詳細(xì)案例指導(dǎo)

    企業(yè)級(jí)項(xiàng)目開發(fā)中保證接口安全的11個(gè)小技巧,詳細(xì)案例指導(dǎo)。 如何保證接口的安全性? 1 參數(shù)校驗(yàn) 保證接口安全的第一步,也是最重要的一步,需要對(duì)接口的請(qǐng)求參數(shù)做校驗(yàn)。 如果我們把接口請(qǐng)求參數(shù)的校驗(yàn)做好了,真的可以攔截大部分的無效請(qǐng)求。 我們可以按如下步驟

    2024年01月23日
    瀏覽(31)
  • qt qtcreator qt+vs 編譯器
關(guān)于QT、QT creator和編譯器,新手入門必知的一些知識(shí)關(guān)于QT、QT creator和編譯器,新手入門必知的一些知識(shí)_qt和qtcreator的區(qū)別_炫彩靈感的博客-CSDN博客

    qt qtcreator qt+vs 編譯器 關(guān)于QT、QT creator和編譯器,新手入門必知的一些知識(shí)關(guān)于QT、QT creator和編譯器,新手入門必知的一些知識(shí)_qt和qtcreator的區(qū)別_炫彩靈感的博客-CSDN博客

    對(duì)于一個(gè)新手而言,基本體會(huì)如下: Qt Creator Qt Creator優(yōu)勢(shì) 可以實(shí)現(xiàn)Ui和代碼無縫切換。(VS不行) 對(duì)于漢字的支持更好 提示功能做的更好。 比如:#include等,敲出#inc即有提示。 qmake非常好用 項(xiàng)目管理更方便,可以添加pri之類的來管理子模塊 Qt Creator劣勢(shì)(IDE本身巨大劣勢(shì))

    2024年02月11日
    瀏覽(87)
  • Java后端開發(fā)面試題——多線程

    Java后端開發(fā)面試題——多線程

    創(chuàng)建線程的方式有哪些? 繼承Thread類 實(shí)現(xiàn)runnable接口 實(shí)現(xiàn)Callable接口 線程池創(chuàng)建線程 runnable 和 callable 有什么區(qū)別? Runnable 接口run方法沒有返回值 Callable接口call方法有返回值,是個(gè)泛型,和Future、FutureTask配合可以用來獲取異步執(zhí)行的結(jié)果 Callable接口的call()方法允許拋出異常

    2024年02月10日
    瀏覽(21)
  • 【Linux后端服務(wù)器開發(fā)】封裝線程池實(shí)現(xiàn)TCP多線程通信

    目錄 一、線程池模塊 Thread.h LockGuard.h ThreadPool.h 二、任務(wù)模塊模塊 Task.h 三、日志模塊 Log.h 四、守護(hù)進(jìn)程模塊 Deamon.h ?五、TCP通信模塊 Server.h Client.h server.cpp client.cpp 關(guān)于TCP通信協(xié)議的封裝,此篇博客有詳述: 【Linux后端服務(wù)器開發(fā)】TCP通信設(shè)計(jì)_命運(yùn)on-9的博客-CSDN博客 線程池

    2024年02月16日
    瀏覽(26)
  • 警惕 Python 中少為人知的十個(gè)安全陷阱(1)

    警惕 Python 中少為人知的十個(gè)安全陷阱(1)

    def touch_tmp_file(request): id = request.GET[‘id’] tmp_file = tempfile.NamedTemporaryFile(prefix=id) return HttpResponse(f\\\"tmp file: {tmp_file} created!\\\", content_type=‘text/plain’) 在第 3 行中,用戶輸入的 id 被當(dāng)作臨時(shí)文件的前綴。如果攻擊者傳入的 id 參數(shù)是“/…/var/www/test”,則會(huì)創(chuàng)建出這樣的臨時(shí)文件:

    2024年04月23日
    瀏覽(20)
  • 后端架構(gòu)師必知必會(huì)系列:網(wǎng)絡(luò)協(xié)議與通信機(jī)制

    作者:禪與計(jì)算機(jī)程序設(shè)計(jì)藝術(shù) 隨著互聯(lián)網(wǎng)的普及和互聯(lián)網(wǎng)服務(wù)平臺(tái)的崛起,越來越多的人開始了解到網(wǎng)絡(luò)協(xié)議和通信機(jī)制。而作為后端開發(fā)工程師,掌握網(wǎng)絡(luò)協(xié)議、通信機(jī)制對(duì)于系統(tǒng)的性能優(yōu)化和網(wǎng)絡(luò)安全來說至關(guān)重要。這幾年,網(wǎng)絡(luò)協(xié)議和通信機(jī)制在各個(gè)領(lǐng)域都得到了廣

    2024年02月07日
    瀏覽(29)
  • C++11原子變量:線程安全、無鎖操作的實(shí)例解析

    C++11原子變量:線程安全、無鎖操作的實(shí)例解析

    ? 在 C++11 中,原子變量( std::atomic )提供了一種線程安全的方式來操作共享變量。下面是一個(gè)簡(jiǎn)單的例子,演示了C++11原子變量的用法。 原子性操作: ?原子變量提供了原子性操作,避免了多線程同時(shí)訪問共享變量時(shí)的競(jìng)爭(zhēng)條件。 無鎖: ?使用原子變量的操作是無鎖的,因

    2024年01月20日
    瀏覽(23)
  • ChatGPT Prompt 提示詞設(shè)計(jì)技巧必知必會(huì)

    ChatGPT Prompt 提示詞設(shè)計(jì)技巧必知必會(huì)

    本文內(nèi)容整理自圖靈社區(qū)直播《朱立成:ChatGPT Prompt提示詞技巧必知必會(huì)》。 朱立成,圖靈社區(qū)《ChatGPT即學(xué)即用》視頻課程作者, 軟件工程師,對(duì)新事物充滿好奇,關(guān)注ChatGPT應(yīng)用。 2001年畢業(yè)于浙江大學(xué),從事軟件開發(fā),參與或主持開發(fā)過PC外設(shè)驅(qū)動(dòng)程序、數(shù)字法庭系統(tǒng)、電

    2024年02月08日
    瀏覽(41)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包