java線程安全的高效計數(java線程安全的計數器)

  • 1、JAVA中如何保證線程安全以及主鍵自增有序
  • 2、如何解決java中線程安全問題
  • 3、java中怎麼既能高並發效率又能線程安全
  • 4、Java中如何保證線程安全性
  • 5、Java中如何使方法線程安全

JAVA中如何保證線程安全以及主鍵自增有序

一、常見場景

多個線程針對一個i進行主鍵自增。多線程下如果不做安全策略,將會導致各個現成獲取的i值重複,導致臟數據

常見策略

1、增加syschroize進行線程同步

2、使用lock、unlock處理

3、使用reetrantent 鎖進行鎖定

缺點:容易造成性能低下,或者編寫代碼容易造成死鎖

二、新方案

jdk新提供的功能,atomicInteger(還有其他一atomic開頭的原子性操作類)

AtomicInteger,一個提供原子操作的Integer的類。在Java語言中,++i和i++操作並不是線程安全的,在使用的時候,不可避免的會用到synchronized關鍵字。而AtomicInteger則通過一種線程安全的加減操作介面。

原理:通過java的CAS compare and swap,簡稱cas原語進行操作提升性能,這個也號稱樂觀鎖,不阻塞

觀鎖實際上並不加鎖,當計算遇到衝突或者說前後不一致時會重試 直到成功

CAS有3個操作數 內存值V 要跟內存值做比較的值A 和 新值 B

[html] view plain copy

while(true){

if(V == A){

V = B;

return ;

}else{

A = V;

}

}

CAS的操作對象為volatile類型

volatile類型變數是:CPU直接讀寫變數所在的內存 而不是把變數copy到寄存器操作

這樣對變數的操作所有線程都是可見的

這樣做的結果是減少了並發時衝突的概率 但不能完全避免

java中,線程安全的解決方法或過程:

1.如果對象是immutable,則是線程安全的,例如:String,可以放心使用。

2. 如果對象是線程安全的,則放心使用。

3.有條件線程安全,對於Vector和Hashtable一般情況下是線程安全的,但是對於某些特殊情況,需要通過額外的synchronized保證線程安全。

4.使用synchronized關鍵字。

總的結論:java是線程安全的,即對任何方法(包括靜態方法)都可以不考慮線程衝突,但有一個前提,就是不能存在全局變數。如果存在全局變數,則需要使用同步機制。

如下通過一組對比例子從頭講解:

在多線程中使用靜態方法會發生什麼事?也就是說多線程訪問同一個類的static靜態方法會發生什麼事?是否會發生線程安全問題?

public class Test {

public static void operation(){

// … do something

}

}

事實證明只要在靜態函數中沒有處理多線程共享數據,就不存在著多線程訪問同一個靜態方法會出現資源衝突的問題。下面看一個例子:

public class StaticThread implements Runnable {

@Override

public void run() {

// TODO Auto-generated method stub

StaticAction.print();

}

public static void main(String[] args) {

for (int i = 0; i 100; i++) {

new Thread(new StaticThread()).start();

}

}

}

public class StaticAction {

public static int i = 0;

public static void print() {

int sum = 0;

for (int i = 0; i 10; i++) {

System.out.print(“step ” + i + ” is running.”);

sum += i;

}

if (sum != 45) {

System.out.println(“Thread error!”);

System.exit(0);

}

System.out.println(“sum is ” + sum);

並發(concurrency)一個並不陌生的詞,簡單來說,就是cpu在同一時刻執行多個任務。

而Java並發則由多線程實現的。

在jvm的世界裡,線程就像不相干的平行空間,串列在虛擬機中。(當然這是比較籠統的說法,線程之間是可以交互的,他們也不一定是串列。)

多線程的存在就是壓榨cpu,提高程序性能,還能減少一定的設計複雜度(用現實的時間思維設計程序)。

這麼說來似乎線程就是傳說中的銀彈了,可事實告訴我們真正的銀彈並不存在。

多線程會引出很多難以避免的問題, 如死鎖,臟數據,線程管理的額外開銷,等等。更大大增加了程序設計的複雜度。

但他的優點依舊不可替代。

死鎖和臟數據就是典型的線程安全問題。

簡單來說,線程安全就是: 在多線程環境中,能永遠保證程序的正確性。

只有存在共享數據時才需要考慮線程安全問題。

java內存區域:

其中, 方法區和堆就是主要的線程共享區域。那麼就是說共享對象只可能是類的屬性域或靜態域。

了解了線程安全問題的一些基本概念後, 我們就來說說如何解決線程安全問題。我們來從一個簡單的servlet示例來分析:

public class ReqCounterServlet extends HttpServlet{    private int count = 0;

public void doGet(HttpServletRequest request,

HttpServletResponse response) throws IOException, ServletException {

count++;

System.out.print(“當前已達到的請求數為” + count);

}

public void doPost(HttpServletRequest request,

HttpServletResponse response) throws IOException, ServletException {        // ignore    }

}

1. 了解業務場景的線程模型

這裡的線程模型指的是: 在該業務場景下, 可能出現的線程調用實況。

眾所周知,Servlet是被設計為單實例,在請求進入tomcat後,由Connector建立連接,再講請求分發給內部線程池中的Processor,

此時Servlet就處於一個多線程環境。即如果存在幾個請求同時訪問某個servlet,就可能會有幾個線程同時訪問該servlet對象。如圖:

線程模型,如果簡單的話,就在腦海模擬一下就好了,複雜的話就可以用紙筆或其他工具畫出來。

2. 找出共享對象

這裡的共享對象就很明顯就是ReqCounterServlet。

3. 分析共享對象的不變性條件

不變性條件,這個名詞是在契約式編程的概念中的。不變性條件保證類的狀態在任何功能被執行後都保持在一個可接受的狀態。

這裡可以引申出, 不可變對象是線程安全的。(因為不可變對象就沒有不變性條件)

不變性條件則主要由對可變狀態的修改與訪問構成。

這裡的servlet很簡單, 不變性條件大致可以歸納為: 每次請求進入時count計數必須加一,且計數必須正確。

在複雜的業務中, 類的不變性條件往往很難考慮周全。設計的世界是險惡的,只能小心謹慎,用測量去證明,最大程度地減少錯誤出現的幾率。

4. 用特定的策略解決線程安全問題。

如何解決的確是該流程的重點。目前分三種方式解決:

第一種,修改線程模型。即不在線程之間共享該狀態變數。一般這個改動比較大,需要量力而行。

第二種,將對象變為不可變對象。有時候實現不了。

第三種,就比較通用了,在訪問狀態變數時使用同步。 synchronized和Lock都可以實現同步。簡單點說,就是在你修改或訪問可變狀態時加鎖,獨佔對象,讓其他線程進不來。

這也算是一種線程隔離的辦法。(這種方式也有不少缺點,比如說死鎖,性能問題等等)

其實有一種更好的辦法,就是設計線程安全類。《代碼大全》就有提過,問題解決得越早,花費的代價就越小。

是的,在設計時,就考慮線程安全問題會容易的多。

首先考慮該類是否會存在於多線程環境。如果不是,則不考慮線程安全。

然後考慮該類是否能設計為不可變對象,或者事實不可變對象。如果是,則不考慮線程安全

最後,根據流程來設計線程安全類。

設計線程安全類流程:

1、找出構成對象狀態的所有變數。

2、找出約束狀態變數的不變性條件。

3、建立對象狀態的並發訪問管理策略。

有兩種常用的並發訪問管理策略:

1、java監視器模式。  一直使用某一對象的鎖來保護某狀態。

2、線程安全委託。  將類的線程安全性委託給某個或多個線程安全的狀態變數。(注意多個時,這些變數必須是彼此獨立,且不存在相關聯的不變性條件。)

線程不安全的場合很多,比如像操作系統中的用戶界面、印表機等外設、控制台輸出,都不允許並發(設想兩個程序同時要輸出文字到同一個屏幕,那還不亂套了)

在代碼中,每個線程有自己的堆棧但是共享整個堆,所以訪問那些全局的變數,也必須同步,否則會出現臟讀數據。

同步也不是萬能的良藥。不當的鎖定會導致程序死鎖,而且多線程本身就是為了提高性能,但是同步使用多了,程序又實質上退化成了單線程程序,用多線程的意義也就沒了。

原創文章,作者:KE1CX,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/127072.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
KE1CX的頭像KE1CX
上一篇 2024-10-03 23:13
下一篇 2024-10-03 23:13

相關推薦

  • Git secbit:一種新型的安全Git版本

    Git secbit是一種新型的安全Git版本,它在保持Git原有功能的同時,針對Git存在的安全漏洞做出了很大的改進。下面我們將從多個方面對Git secbit做詳細地闡述。 一…

    編程 2025-04-29
  • Python線程等待指南

    本文將從多個方面詳細講解Python線程等待的相關知識。 一、等待線程結束 在多線程編程中,經常需要等待線程執行完畢再進行下一步操作。可以使用join()方法實現等待線程執行完畢再…

    編程 2025-04-29
  • Python兩個線程交替列印1到100

    這篇文章的主題是關於Python多線程的應用。我們將會通過實際的代碼,學習如何使用Python兩個線程交替列印1到100。 一、創建線程 在Python中,我們可以使用Thread…

    編程 2025-04-28
  • Python每次運行變數加一:實現計數器功能

    Python編程語言中,每次執行程序都需要定義變數,而在實際開發中常常需要對變數進行計數或者累加操作,這時就需要了解如何在Python中實現計數器功能。本文將從以下幾個方面詳細講解…

    編程 2025-04-28
  • Trocket:打造高效可靠的遠程控制工具

    如何使用trocket打造高效可靠的遠程控制工具?本文將從以下幾個方面進行詳細的闡述。 一、安裝和使用trocket trocket是一個基於Python實現的遠程控制工具,使用時…

    編程 2025-04-28
  • ROS線程發布消息異常解決方法

    針對ROS線程發布消息異常問題,我們可以從以下幾個方面進行分析和解決。 一、檢查ROS代碼是否正確 首先,我們需要檢查ROS代碼是否正確。可能會出現的問題包括: 是否正確初始化RO…

    編程 2025-04-28
  • 手機安全模式怎麼解除?

    安全模式是一種手機自身的保護模式,它會禁用第三方應用程序並使用僅限基本系統功能。但有時候,安全模式會使你無法使用手機上的一些重要功能。如果你想解除手機安全模式,可以嘗試以下方法: …

    編程 2025-04-28
  • Powersploit:安全評估與滲透測試的利器

    本文將重點介紹Powersploit,並給出相關的完整的代碼示例,幫助安全人員更好地運用Powersploit進行安全評估和滲透測試。 一、Powersploit簡介 Powers…

    編程 2025-04-28
  • Python生成列表最高效的方法

    本文主要介紹在Python中生成列表最高效的方法,涉及到列表生成式、range函數、map函數以及ITertools模塊等多種方法。 一、列表生成式 列表生成式是Python中最常…

    編程 2025-04-28
  • Python線程池並發爬蟲

    Python線程池並發爬蟲是實現多線程爬取數據的常用技術之一,可以在一定程度上提高爬取效率和數據處理能力。本文將從多個方面對Python線程池並發爬蟲做詳細的闡述,包括線程池的實現…

    編程 2025-04-27

發表回復

登錄後才能評論