當前位置:首頁 » 文件管理 » java多線程使用緩存

java多線程使用緩存

發布時間: 2022-08-09 00:15:10

java多線程訪問資料庫怎麼優化啊,並發很大

個人覺得高寫入並發的話先用緩存緩沖一下,可以合並的寫入合並成批量寫入可以管一些用但終歸寫入量很大的話還是要在資料庫端優化了,把並發寫均衡到多台伺服器上,應該沒有別的辦法了。如果瓶頸不再資料庫那就是應用伺服器處理能力不足,升級應用伺服器。

Ⅱ java如何實現線程安全的緩存

簡單來說就是多線程的時候,多線程同時修改同一個類的時候,由於訪問順序隨機導致類功能出錯,至於線程安全類設計方法很多的,Java可以用synchronize標識類,只允許一個線程在同一時間訪問它,選擇線程安全的數據類型例如ArrayList,數組是不安全的,你可以多去網路查!

Ⅲ 最近跟著java的API學習了多線程,遇到了一個API自帶的例子,例子的功能是實現一個緩存機制。代碼如下:

為了多次循環啊。。。。if是條件判斷。。。。

Ⅳ java 多線程的應用程序(以後要添加到服務里),有沒有緩存的概念

可以在線程中返回你所有對象的單例。在線程中將你的對象聲明為static,然後建一個方法,如
public static getXX(){
if(XX==null)
{
XX=new XX();

}
return XX;//XX是你所要的對象

}

補充-------------
為什麼沒用? 那樣你可以在你的對象裡面同樣使用單例,然後把上邊的代碼中的new的部分改成調用單例方法的部分不就行了

Ⅳ java多線程有幾種實現方法線程之間如何同步

一、為什麼要線程同步

因為當我們有多個線程要同時訪問一個變數或對象時,如果這些線程中既有讀又有寫操作時,就會導致變數值或對象的狀態出現混亂,從而導致程序異常。舉個例子,如果一個銀行賬戶同時被兩個線程操作,一個取100塊,一個存錢100塊。假設賬戶原本有0塊,如果取錢線程和存錢線程同時發生,會出現什麼結果呢?取錢不成功,賬戶余額是100.取錢成功了,賬戶余額是0.那到底是哪個呢?很難說清楚。因此多線程同步就是要解決這個問題。

二、不同步時的代碼

Bank.Java

packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//賬戶余額

//存錢
publicvoidaddMoney(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}

//取錢
publicvoidsubMoney(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}

SyncThreadTest.java


packagethreadTest;


publicclassSyncThreadTest{

publicstaticvoidmain(Stringargs[]){
finalBankbank=newBank();

Threadtadd=newThread(newRunnable(){

@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
bank.addMoney(100);
bank.lookMoney();
System.out.println(" ");

}
}
});

Threadtsub=newThread(newRunnable(){

@Override
publicvoidrun(){
//TODOAuto-generatedmethodstub
while(true){
bank.subMoney(100);
bank.lookMoney();
System.out.println(" ");
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
});
tsub.start();

tadd.start();
}



}

余額不足
賬戶余額:0


余額不足
賬戶余額:100


1441790503354存進:100
賬戶余額:100


1441790504354存進:100
賬戶余額:100


1441790504354取出:100
賬戶余額:100


1441790505355存進:100
賬戶余額:100


1441790505355取出:100
賬戶余額:100

三、使用同步時的代碼

(1)同步方法:


即有synchronized關鍵字修飾的方法。由於java的每個對象都有一個內置鎖,當用此關鍵字修飾方法時,內置鎖會保護整個方法。在調用該方法前,需要獲得內置鎖,否則就處於阻塞狀態。

修改後的Bank.java


packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//賬戶余額

//存錢
(intmoney){
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}

//取錢
(intmoney){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}

再看看運行結果:


余額不足
賬戶余額:0


余額不足
賬戶余額:0


1441790837380存進:100
賬戶余額:100


1441790838380取出:100
賬戶余額:0
1441790838380存進:100
賬戶余額:100
1441790839381取出:100
賬戶余額:0

瞬間感覺可以理解了吧。


註: synchronized關鍵字也可以修飾靜態方法,此時如果調用該靜態方法,將會鎖住整個類

(2)同步代碼塊

即有synchronized關鍵字修飾的語句塊。被該關鍵字修飾的語句塊會自動被加上內置鎖,從而實現同步

Bank.java代碼如下:


packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//賬戶余額

//存錢
publicvoidaddMoney(intmoney){

synchronized(this){
count+=money;
}
System.out.println(System.currentTimeMillis()+"存進:"+money);
}

//取錢
publicvoidsubMoney(intmoney){

synchronized(this){
if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
}
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}

運行結果如下:

余額不足
賬戶余額:0


1441791806699存進:100
賬戶余額:100


1441791806700取出:100
賬戶余額:0


1441791807699存進:100
賬戶余額:100

效果和方法一差不多。

註:同步是一種高開銷的操作,因此應該盡量減少同步的內容。通常沒有必要同步整個方法,使用synchronized代碼塊同步關鍵代碼即可。

(3)使用特殊域變數(volatile)實現線程同步


a.volatile關鍵字為域變數的訪問提供了一種免鎖機制
b.使用volatile修飾域相當於告訴虛擬機該域可能會被其他線程更新
c.因此每次使用該域就要重新計算,而不是使用寄存器中的值
d.volatile不會提供任何原子操作,它也不能用來修飾final類型的變數

Bank.java代碼如下:


packagethreadTest;

/**
*@authorww
*
*/
publicclassBank{

privatevolatileintcount=0;//賬戶余額

//存錢
publicvoidaddMoney(intmoney){

count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);
}

//取錢
publicvoidsubMoney(intmoney){

if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}

運行效果怎樣呢?


余額不足
賬戶余額:0


余額不足
賬戶余額:100


1441792010959存進:100
賬戶余額:100


1441792011960取出:100
賬戶余額:0


1441792011961存進:100
賬戶余額:100

是不是又看不懂了,又亂了。這是為什麼呢?就是因為volatile不能保證原子操作導致的,因此volatile不能代替synchronized。此外volatile會組織編譯器對代碼優化,因此能不使用它就不適用它吧。它的原理是每次要線程要訪問volatile修飾的變數時都是從內存中讀取,而不是存緩存當中讀取,因此每個線程訪問到的變數值都是一樣的。這樣就保證了同步。


(4)使用重入鎖實現線程同步

在JavaSE5.0中新增了一個java.util.concurrent包來支持同步。ReentrantLock類是可重入、互斥、實現了Lock介面的鎖,它與使用synchronized方法和快具有相同的基本行為和語義,並且擴展了其能力。
ReenreantLock類的常用方法有:
ReentrantLock() : 創建一個ReentrantLock實例
lock() : 獲得鎖
unlock() : 釋放鎖
註:ReentrantLock()還有一個可以創建公平鎖的構造方法,但由於能大幅度降低程序運行效率,不推薦使用
Bank.java代碼修改如下:


packagethreadTest;

importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;

/**
*@authorww
*
*/
publicclassBank{

privateintcount=0;//賬戶余額

//需要聲明這個鎖
privateLocklock=newReentrantLock();

//存錢
publicvoidaddMoney(intmoney){
lock.lock();//上鎖
try{
count+=money;
System.out.println(System.currentTimeMillis()+"存進:"+money);

}finally{
lock.unlock();//解鎖
}
}

//取錢
publicvoidsubMoney(intmoney){
lock.lock();
try{

if(count-money<0){
System.out.println("余額不足");
return;
}
count-=money;
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}finally{
lock.unlock();
}
}

//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count);
}
}

運行效果怎麼樣呢?


余額不足
賬戶余額:0


余額不足
賬戶余額:0


1441792891934存進:100
賬戶余額:100


1441792892935存進:100
賬戶余額:200


1441792892954取出:100
賬戶余額:100

效果和前兩種方法差不多。


如果synchronized關鍵字能滿足用戶的需求,就用synchronized,因為它能簡化代碼 。如果需要更高級的功能,就用ReentrantLock類,此時要注意及時釋放鎖,否則會出現死鎖,通常在finally代碼釋放鎖

(5)使用局部變數實現線程同步


Bank.java代碼如下:


packagethreadTest;


/**
*@authorww
*
*/
publicclassBank{

privatestaticThreadLocal<Integer>count=newThreadLocal<Integer>(){

@Override
protectedIntegerinitialValue(){
//TODOAuto-generatedmethodstub
return0;
}

};


//存錢
publicvoidaddMoney(intmoney){
count.set(count.get()+money);
System.out.println(System.currentTimeMillis()+"存進:"+money);

}

//取錢
publicvoidsubMoney(intmoney){
if(count.get()-money<0){
System.out.println("余額不足");
return;
}
count.set(count.get()-money);
System.out.println(+System.currentTimeMillis()+"取出:"+money);
}

//查詢
publicvoidlookMoney(){
System.out.println("賬戶余額:"+count.get());
}
}

運行效果:


余額不足
賬戶余額:0


余額不足
賬戶余額:0


1441794247939存進:100
賬戶余額:100


余額不足
1441794248940存進:100
賬戶余額:0


賬戶余額:200


余額不足
賬戶余額:0


1441794249941存進:100
賬戶余額:300

看了運行效果,一開始一頭霧水,怎麼只讓存,不讓取啊?看看ThreadLocal的原理:


如果使用ThreadLocal管理變數,則每一個使用該變數的線程都獲得該變數的副本,副本之間相互獨立,這樣每一個線程都可以隨意修改自己的變數副本,而不會對其他線程產生影響。現在明白了吧,原來每個線程運行的都是一個副本,也就是說存錢和取錢是兩個賬戶,知識名字相同而已。所以就會發生上面的效果。


ThreadLocal與同步機制
a.ThreadLocal與同步機制都是為了解決多線程中相同變數的訪問沖突問題

b.前者採用以"空間換時間"的方法,後者採用以"時間換空間"的方式

Ⅵ Java多線程是什麼意思

Java多線程實現方式主要有三種:繼承Thread類、實現Runnable介面、使用ExecutorService、Callable、Future實現有返回結果的多線程。其中前兩種方式線程執行完後都沒有返回值,只有最後一種是帶返回值的。

1、繼承Thread類實現多線程
繼承Thread類的方法盡管被我列為一種多線程實現方式,但Thread本質上也是實現了Runnable介面的一個實例,它代表一個線程的實例,並且,啟動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啟動一個新線程,並執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend Thread,並復寫run()方法,就可以啟動新線程並執行自己定義的run()方法。例如:

代碼說明:
上述代碼中Executors類,提供了一系列工廠方法用於創先線程池,返回的線程池都實現了ExecutorService介面。
public static ExecutorService newFixedThreadPool(int nThreads)
創建固定數目線程的線程池。
public static ExecutorService newCachedThreadPool()
創建一個可緩存的線程池,調用execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鍾未被使用的線程。
public static ExecutorService newSingleThreadExecutor()
創建一個單線程化的Executor。
public static ScheledExecutorService newScheledThreadPool(int corePoolSize)
創建一個支持定時及周期性的任務執行的線程池,多數情況下可用來替代Timer類。

總結:ExecutoreService提供了submit()方法,傳遞一個Callable,或Runnable,返回Future。如果Executor後台線程池還沒有完成Callable的計算,這調用返回Future對象的get()方法,會阻塞直到計算完成。

Ⅶ java多線程的問題

import java.util.concurrent.ArrayBlockingQueue;
public class ArrayBlockingQueueTest {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<Object> queue = new ArrayBlockingQueue(1000);
queue.put("123");
Object value = queue.take();
System.out.println(value);
}
}

你的這個問題,是大部分多線程程序都會遇到的問題,因為JDK類庫的開發者已經為我們准備了很多「隊列」。


你可以考慮「先進先出阻塞隊列」。


ArrayBlockingQueue<E>

一個由數組支持的有界阻塞隊列。此隊列按FIFO(先進先出)原則對元素進行排序。隊列的頭部是在隊列中存在時間最長的元素。隊列的尾部是在隊列中存在時間最短的元素。新元素插入到隊列的尾部,隊列獲取操作則是從隊列頭部開始獲得元素。


這是一個典型的「有界緩存區」,固定大小的數組在其中保持生產者插入的元素和使用者提取的元素。一旦創建了這樣的緩存區,就不能再增加其容量。試圖向已滿隊列中放入元素會導致操作受阻塞;試圖從空隊列中提取元素將導致類似阻塞。


一些重要的方法:

boolean add(Ee) 將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量),在成功時返回true,如果此隊列已滿,則拋出IllegalStateException。


boolean offer(Ee) 將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量),在成功時返回true,如果此隊列已滿,則返回false。


boolean offer(Ee,longtimeout,TimeUnitunit) 將指定的元素插入此隊列的尾部,如果該隊列已滿,則在到達指定的等待時間之前等待可用的空間。


void put(Ee) 將指定的元素插入此隊列的尾部,如果該隊列已滿,則等待可用的空間。


----


E remove() 獲取並移除此隊列的頭。


E poll() 獲取並移除此隊列的頭,如果此隊列為空,則返回null。


E poll(longtimeout,TimeUnitunit) 獲取並移除此隊列的頭部,在指定的等待時間前等待可用的元素(如果有必要)。


E take() 獲取並移除此隊列的頭部,在元素變得可用之前一直等待(如果有必要)。

Ⅷ java 多線程怎麼深入

並發與並行

並行,表示兩個線程同時做事情。

並發,表示一會做這個事情,一會做另一個事情,存在著調度。單核 CPU 不可能存在並行(微觀上)。

image

以上就是原生線程池創建的核心原理。除了原生線程池之外並發包還提供了簡單的創建方式,上面也說了它們是對原生線程池的一種包裝,可以讓開發者簡單快捷的創建所需要的線程池。

Executors

newSingleThreadExecutor

創建一個線程的線程池,在這個線程池中始終只有一個線程存在。如果線程池中的線程因為異常問題退出,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。

newFixedThreadPool

創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執行異常而結束,那麼線程池會補充一個新線程。

newCachedThreadPool

可根據實際情況,調整線程數量的線程池,線程池中的線程數量不確定,如果有空閑線程會優先選擇空閑線程,如果沒有空閑線程並且此時有任務提交會創建新的線程。在正常開發中並不推薦這個線程池,因為在極端情況下,會因為 newCachedThreadPool 創建過多線程而耗盡 CPU 和內存資源。

newScheledThreadPool

此線程池可以指定固定數量的線程來周期性的去執行。比如通過 scheleAtFixedRate 或者 scheleWithFixedDelay 來指定周期時間。

PS:另外在寫定時任務時(如果不用 Quartz 框架),最好採用這種線程池來做,因為它可以保證裡面始終是存在活的線程的。

推薦使用 ThreadPoolExecutor 方式

在阿里的 Java 開發手冊時有一條是不推薦使用 Executors 去創建,而是推薦去使用 ThreadPoolExecutor 來創建線程池。

這樣做的目的主要原因是:使用 Executors 創建線程池不會傳入核心參數,而是採用的默認值,這樣的話我們往往會忽略掉裡面參數的含義,如果業務場景要求比較苛刻的話,存在資源耗盡的風險;另外採用 ThreadPoolExecutor 的方式可以讓我們更加清楚地了解線程池的運行規則,不管是面試還是對技術成長都有莫大的好處。

改了變數,其他線程可以立即知道。保證可見性的方法有以下幾種:

  • volatile

  • 加入 volatile 關鍵字的變數在進行匯編時會多出一個 lock 前綴指令,這個前綴指令相當於一個內存屏障,內存屏障可以保證內存操作的順序。當聲明為 volatile 的變數進行寫操作時,那麼這個變數需要將數據寫到主內存中。

    由於處理器會實現緩存一致性協議,所以寫到主內存後會導致其他處理器的緩存無效,也就是線程工作內存無效,需要從主內存中重新刷新數據。

    Ⅸ java多線程寫文件,同步問題。

    說下我的想法吧:編寫一個類,這個類的功能是用多線程將多個數據流按順序緩沖排列合並到緩存中,另外啟動一個線程負責將緩存數據寫入指定的文件中。

    熱點內容
    現在玩游戲的電腦需要什麼配置 發布:2024-04-20 17:09:57 瀏覽:194
    游樂園的密碼一般為多少 發布:2024-04-20 17:09:51 瀏覽:40
    興元安卓機怎麼進系統 發布:2024-04-20 17:07:16 瀏覽:805
    我的世界伺服器如何放村民 發布:2024-04-20 17:05:35 瀏覽:358
    手機反編譯dex 發布:2024-04-20 17:01:01 瀏覽:703
    安卓怎麼設置微信拍一拍 發布:2024-04-20 16:44:48 瀏覽:568
    三星3熱點密碼怎麼設置 發布:2024-04-20 16:30:52 瀏覽:578
    用keil編譯顯示警告warn 發布:2024-04-20 16:27:09 瀏覽:893
    訪問在哪兒 發布:2024-04-20 16:20:42 瀏覽:200
    安卓手機有什麼可以把聲音改成電音的軟體 發布:2024-04-20 16:19:40 瀏覽:563