導航:首頁 > 編程語言 > java隊列同步

java隊列同步

發布時間:2024-05-31 04:22:04

java阻塞隊列 線程同步合作

Queue介面與List Set同一級別 都是繼承了Collection介面 LinkedList實現了Queue介面 Queue介面窄化了對LinkedList的方法的訪問許可權(即在方法中的參數類型如果是Queue時 就完全只能訪問Queue介面所定義的方法了 而不能直接訪問 LinkedList的非Queue的方法) 以使得只有恰當的方法才可以使用 BlockingQueue 繼承了Queue介面

隊列是一種數據結構.它有兩個基本操作 在隊列尾部加人一個元素 和從隊列頭部移除一個元素就是說 隊列以一種先進先出的方式管理數據 如果你試圖向一個已經滿了的阻塞隊列中添加一個元素或者是從一個空的阻塞隊列中移除一個元索 將導致線程阻塞.在多線程進行合作時 阻塞隊列是很有用的工具 工作者線程可以定期地把中間結果存到阻塞隊列中而其他工作者線線程把中間結果取出並在將來修改它們 隊列會自動平衡負載 如果第一個線程集運行得比第二個慢 則第二個線程集在等待結果時就會阻塞 如果第一個線程集運行得快 那麼它將等待第二個線程集趕上來 下表顯示了jdk 中的阻塞隊列的操作

add 增加一個元索 如果隊列已春慎橋滿 則拋出一個IIIegaISlabEepeplian異常

remove 移除並返回隊列頭部的元素 如果隊列為空 則拋出一個NoSuchElementException異常

element 返回隊列頭部的元素 如果隊列為空 則拋出一個NoSuchElementException異常

offer 添加一個元素並返回true 如果隊列已滿 則返回false

poll 移除並返問隊列頭部的元素 如果隊列扒猛為空 則返回null

peek 返回隊列頭部的元素 如果隊列為空 則返回null

put 添加一個元素 如果隊列滿 則阻塞

take 移除並返回隊列頭部的元素 如果隊列為空 則阻塞

remove element offer poll peek 其實是屬於Queue介面

阻塞隊列的操作可以根據它們的響應方式分為以下三類 aad removee和element操作在你試圖為一個已滿的隊列增加元素或從空隊列取得元素時拋出異常 當然 在多線程程序中 隊列在任何時間都可能變成滿的或空的 所以你可能想使用offer poll peek方法 這些方法在無法完成任務時只是給出一個出錯示而不會拋出異常

注意 poll和peek方法出錯進返回null 因此 向隊列中插入null值是不合法的

還有帶超時的offer和poll方法變種 例如 下面的調用

boolean success = q offer(x TimeUnit MILLISECONDS);

嘗試在 毫秒內向隊列尾部插入一個元素 如果成功 立即返回true 否則 當到達超時進 返回false 同樣地 調用

Object head = q poll( TimeUnit MILLISECONDS);

如果在 毫秒內成功地移除了隊列頭元素 則立即返回頭元素 否則在到達超時時 返回null

最後 我們有阻塞操作put和take put方法在隊列滿時阻塞 take方法在隊列空時阻塞

ncurrent包提供了阻塞隊列的 個變種 默認情況下 LinkedBlockingQueue的容量是沒有上限的(說的不準確 在不指定時容量為Integer MAX_VALUE 不要然的話在put時怎麼會受阻呢) 但是也可以選擇指定其最大容量 它是基孝仔於鏈表的隊列 此隊列按 FIFO(先進先出)排序元素

ArrayBlockingQueue在構造時需要指定容量 並可以選擇是否需要公平性 如果公平參數被設置true 等待時間最長的線程會優先得到處理(其實就是通過將ReentrantLock設置為true來達到這種公平性的 即等待時間最長的線程會先操作) 通常 公平性會使你在性能上付出代價 只有在的確非常需要的時候再使用它 它是基於數組的阻塞循環隊列 此隊列按 FIFO(先進先出)原則對元素進行排序

PriorityBlockingQueue是一個帶優先順序的隊列 而不是先進先出隊列 元素按優先順序順序被移除 該隊列也沒有上限(看了一下源碼 PriorityBlockingQueue是對PriorityQueue的再次包裝 是基於堆數據結構的 而PriorityQueue是沒有容量限制的 與ArrayList一樣 所以在優先阻塞隊列上put時是不會受阻的 雖然此隊列邏輯上是無界的 但是由於資源被耗盡 所以試圖執行添加操作可能會導致 OutOfMemoryError) 但是如果隊列為空 那麼取元素的操作take就會阻塞 所以它的檢索操作take是受阻的 另外 往入該隊列中的元素要具有比較能力

最後 DelayQueue(基於PriorityQueue來實現的)是一個存放Delayed 元素的無界阻塞隊列 只有在延遲期滿時才能從中提取元素 該隊列的頭部是延遲期滿後保存時間最長的 Delayed 元素 如果延遲都還沒有期滿 則隊列沒有頭部 並且poll將返回null 當一個元素的 getDelay(TimeUnit NANOSECONDS) 方法返回一個小於或等於零的值時 則出現期滿 poll就以移除這個元素了 此隊列不允許使用 null 元素 下面是延遲介面

Java代碼

public interface Delayed extends Comparable<Delayed> {

long getDelay(TimeUnit unit);

}

public interface Delayed extends Comparable<Delayed> {

long getDelay(TimeUnit unit);

}

放入DelayQueue的元素還將要實現pareTo方法 DelayQueue使用這個來為元素排序

下面的實例展示了如何使用阻塞隊列來控制線程集 程序在一個目錄及它的所有子目錄下搜索所有文件 列印出包含指定關鍵字的文件列表 從下面實例可以看出 使用阻塞隊列兩個顯著的好處就是 多線程操作共同的隊列時不需要額外的同步 另外就是隊列會自動平衡負載 即那邊(生產與消費兩邊)處理快了就會被阻塞掉 從而減少兩邊的處理速度差距 下面是具體實現

Java代碼

public class BlockingQueueTest {

public static void main(String[] args) {

Scanner in = new Scanner(System in);

System out print( Enter base directory (e g /usr/local/jdk /src): );

String directory = in nextLine();

System out print( Enter keyword (e g volatile): );

String keyword = in nextLine();

final int FILE_QUEUE_SIZE = ;// 阻塞隊列大小

final int SEARCH_THREADS = ;// 關鍵字搜索線程個數

// 基於ArrayBlockingQueue的阻塞隊列

BlockingQueue<File> queue = new ArrayBlockingQueue<File>(

FILE_QUEUE_SIZE);

//只啟動一個線程來搜索目錄

FileEnumerationTask enumerator = new FileEnumerationTask(queue

new File(directory));

new Thread(enumerator) start();

//啟動 個線程用來在文件中搜索指定的關鍵字

for (int i = ; i <= SEARCH_THREADS; i++)

new Thread(new SearchTask(queue keyword)) start();

}

}

class FileEnumerationTask implements Runnable {

//啞元文件對象 放在阻塞隊列最後 用來標示文件已被遍歷完

public static File DUMMY = new File( );

private BlockingQueue<File> queue;

private File startingDirectory;

public FileEnumerationTask(BlockingQueue<File> queue File startingDirectory) {

this queue = queue;

this startingDirectory = startingDirectory;

}

public void run() {

try {

enumerate(startingDirectory);

queue put(DUMMY);//執行到這里說明指定的目錄下文件已被遍歷完

} catch (InterruptedException e) {

}

}

// 將指定目錄下的所有文件以File對象的形式放入阻塞隊列中

public void enumerate(File directory) throws InterruptedException {

File[] files = directory listFiles();

for (File file : files) {

if (file isDirectory())

enumerate(file);

else

//將元素放入隊尾 如果隊列滿 則阻塞

queue put(file);

}

}

}

class SearchTask implements Runnable {

private BlockingQueue<File> queue;

private String keyword;

public SearchTask(BlockingQueue<File> queue String keyword) {

this queue = queue;

this keyword = keyword;

}

public void run() {

try {

boolean done = false;

while (!done) {

//取出隊首元素 如果隊列為空 則阻塞

File file = queue take();

if (file == FileEnumerationTask DUMMY) {

//取出來後重新放入 好讓其他線程讀到它時也很快的結束

queue put(file);

done = true;

} else

search(file);

}

} catch (IOException e) {

e printStackTrace();

} catch (InterruptedException e) {

}

}

public void search(File file) throws IOException {

Scanner in = new Scanner(new FileInputStream(file));

int lineNumber = ;

while (in hasNextLine()) {

lineNumber++;

String line = in nextLine();

if (ntains(keyword))

System out printf( %s:%d:%s%n file getPath() lineNumber

line);

}

in close();

}

lishixin/Article/program/Java/hx/201311/26657

⑵ JAVA中哪個能同時滿足 先進先出(增減操作非常頻繁)和同步安全的容器集合並在性能上不至於太差

public class ConcurrentLinkedQueue<E>
extends AbstractQueue<E>
implements Queue<E>, Serializable

一個基於鏈接節點的無界線程安全隊列。此隊列按照 FIFO(先進先出)原則對元素進行排序。隊列的頭部 是隊列中時間最長的元素。隊列的尾部 是隊列中時間最短的元素。新的元素插入到隊列的尾部,隊列獲取操作從隊列頭部獲得元素。當多個線程共享訪問一個公共 collection 時,ConcurrentLinkedQueue 是一個恰當的選擇。此隊列不允許使用 null 元素。

此實現採用了有效的「無等待 (wait-free)」演算法,該演算法基於 Maged M. Michael 和 Michael L. Scott 合著的 Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms 中描述的演算法。

需要小心的是,與大多數 collection 不同,size 方法不是 一個固定時間操作。由於這些隊列的非同步特性,確定當前元素的數量需要遍歷這些元素。

此類及其迭代器實現了 Collection 和 Iterator 介面的所有可選 方法。

內存一致性效果:當存在其他並發 collection 時,將對象放入 ConcurrentLinkedQueue 之前的線程中的操作 happen-before 隨後通過另一線程從 ConcurrentLinkedQueue 訪問或移除該元素的操作。

注意:ConcurrentLinkedQueue的size()是要遍歷一遍集合的!因此,若不能滿足你,可以基於 LinkedList(先進先出),自己加上同步,要性能控制住,需要盡可能小力度加同步 。

⑶ 在JAVA中怎麼實現消息隊列

java中的消息隊列
消息隊列是線程間通訊的手段:

importjava.util.*

publicclassMsgQueue{

privateVectorqueue=null;
publicMsgQueue(){
queue=newVector();
}
publicsynchronizedvoidsend(Objecto)
{
queue.addElement(o);
}
publicsynchronizedObjectrecv()
{
if(queue.size()==0)
returnnull;
Objecto=queue.firstElement();
queue.removeElementAt(0);//orqueue[0]=nullcanalsowork
returno;
}
}

因為java中是lockedbyobject的所以添加synchronized就可以用於線程同步鎖定對象
可以作為多線程處理多任務的存放task的隊列。他的client包括封裝好的task類以及thread類

Java的多線程-線程間的通信2009-08-2521:58
1.線程的幾種狀態
線程有四種狀態,任何一個線程肯定處於這四種狀態中的一種:
1)產生(New):線程對象已經產生,但尚未被啟動,所以無法執行。如通過new產生了一個線程對象後沒對它調用start()函數之前。
2)可執行(Runnable):每個支持多線程的系統都有一個排程器,排程器會從線程池中選擇一個線程並啟動它。當一個線程處於可執行狀態時,表示它可能正處於線程池中等待排排程器啟動它;也可能它已正在執行。如執行了一個線程對象的start()方法後,線程就處於可執行狀態,但顯而易見的是此時線程不一定正在執行中。
3)死亡(Dead):當一個線程正常結束,它便處於死亡狀態。如一個線程的run()函數執行完畢後線程就進入死亡狀態。
4)停滯(Blocked):當一個線程處於停滯狀態時,系統排程器就會忽略它,不對它進行排程。當處於停滯狀態的線程重新回到可執行狀態時,它有可能重新執行。如通過對一個線程調用wait()函數後,線程就進入停滯狀態,只有當兩次對該線程調用notify或notifyAll後它才能兩次回到可執行狀態。
2.classThread下的常用函數函數
2.1suspend()、resume()
1)通過suspend()函數,可使線程進入停滯狀態。通過suspend()使線程進入停滯狀態後,除非收到resume()消息,否則該線程不會變回可執行狀態。
2)當調用suspend()函數後,線程不會釋放它的「鎖標志」。
例11:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
if(shareVar==0){
for(inti=0;i<5;i++){
shareVar++;
if(shareVar==5){
this.suspend();//(1)
}}}
else{
System.out.print(Thread.currentThread().getName());
System.out.println("shareVar="+shareVar);
this.resume();//(2)
}}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();//(5)
//t1.start();//(3)
t2.start();//(4)
}}
運行結果為:
t2shareVar=5
i.當代碼(5)的t1所產生的線程運行到代碼(1)處時,該線程進入停滯狀態。然後排程器從線程池中喚起代碼(4)的t2所產生的線程,此時shareVar值不為0,所以執行else中的語句。
ii.也許你會問,那執行代碼(2)後為什麼不會使t1進入可執行狀態呢?正如前面所說,t1和t2是兩個不同對象的線程,而代碼(1)和(2)都只對當前對象進行操作,所以t1所產生的線程執行代碼(1)的結果是對象t1的當前線程進入停滯狀態;而t2所產生的線程執行代碼(2)的結果是把對象t2中的所有處於停滯狀態的線程調回到可執行狀態。
iii.那現在把代碼(4)注釋掉,並去掉代碼(3)的注釋,是不是就能使t1重新回到可執行狀態呢?運行結果是什麼也不輸出。為什麼會這樣呢?也許你會認為,當代碼(5)所產生的線程執行到代碼(1)時,它進入停滯狀態;而代碼(3)所產生的線程和代碼(5)所產生的線程是屬於同一個對象的,那麼就當代碼(3)所產生的線程執行到代碼(2)時,就可使代碼(5)所產生的線程執行回到可執行狀態。但是要清楚,suspend()函數只是讓當前線程進入停滯狀態,但並不釋放當前線程所獲得的「鎖標志」。所以當代碼(5)所產生的線程進入停滯狀態時,代碼(3)所產生的線程仍不能啟動,因為當前對象的「鎖標志」仍被代碼(5)所產生的線程佔有。
#p#2.2sleep()
1)sleep()函數有一個參數,通過參數可使線程在指定的時間內進入停滯狀態,當指定的時間過後,線程則自動進入可執行狀態。
2)當調用sleep()函數後,線程不會釋放它的「鎖標志」。
例12:
{
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
for(inti=0;i<3;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
try{
Thread.sleep(100);//(4)
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}}}
}
publicclassTestThread{publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();(1)
t1.start();(2)
//t2.start();(3)
}}
運行結果為:
t1:0
t1:1
t1:2
t1:0
t1:1
t1:2
由結果可證明,雖然在run()中執行了sleep(),但是它不會釋放對象的「鎖標志」,所以除非代碼(1)的線程執行完run()函數並釋放對象的「鎖標志」,否則代碼(2)的線程永遠不會執行。
如果把代碼(2)注釋掉,並去掉代碼(3)的注釋,結果將變為:
t1:0
t2:0
t1:1
t2:1
t1:2
t2:2
由於t1和t2是兩個對象的線程,所以當線程t1通過sleep()進入停滯時,排程器會從線程池中調用其它的可執行線程,從而t2線程被啟動。
例13:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
for(inti=0;i<5;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
try{
if(Thread.currentThread().getName().equals("t1"))
Thread.sleep(200);
else
Thread.sleep(100);
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}}
}}
publicclassTestThread{publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();
//t1.start();
t2.start();
}}
運行結果為:
t1:0
t2:0
t2:1
t1:1
t2:2
t2:3
t1:2
t2:4
t1:3
t1:4
由於線程t1調用了sleep(200),而線程t2調用了sleep(100),所以線程t2處於停滯狀態的時間是線程t1的一半,從從結果反映出來的就是線程t2列印兩倍次線程t1才列印一次。
#p#2.3yield()
1)通過yield()函數,可使線程進入可執行狀態,排程器從可執行狀態的線程中重新進行排程。所以調用了yield()的函數也有可能馬上被執行。
2)當調用yield()函數後,線程不會釋放它的「鎖標志」。
例14:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){super(name);
}
publicsynchronizedvoidrun(){for(inti=0;i<4;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
Thread.yield();
}}
}
publicclassTestThread{publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();
t1.start();//(1)
//t2.start();(2)
}
}
運行結果為:
t1:0
t1:1
t1:2
t1:3
t1:0
t1:1
t1:2
t1:3
從結果可知調用yield()時並不會釋放對象的「鎖標志」。
如果把代碼(1)注釋掉,並去掉代碼(2)的注釋,結果為:
t1:0
t1:1
t2:0
t1:2
t2:1
t1:3
t2:2
t2:3
從結果可知,雖然t1線程調用了yield(),但它馬上又被執行了。
2.4sleep()和yield()的區別
1)sleep()使當前線程進入停滯狀態,所以執行sleep()的線程在指定的時間內肯定不會執行;yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態後馬上又被執行。
2)sleep()可使優先順序低的線程得到執行的機會,當然也可以讓同優先順序和高優先順序的線程有執行的機會;yield()只能使同優先順序的線程有執行的機會。
例15:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicvoidrun(){
for(inti=0;i<4;i++){
System.out.print(Thread.currentThread().getName());
System.out.println(":"+i);
//Thread.yield();(1)
/*(2)*/
try{
Thread.sleep(3000);
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}}}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
}
}
運行結果為:
t1:0
t1:1
t2:0
t1:2
t2:1
t1:3
t2:2
t2:3
由結果可見,通過sleep()可使優先順序較低的線程有執行的機會。注釋掉代碼(2),並去掉代碼(1)的注釋,結果為:
t1:0
t1:1
t1:2
t1:3
t2:0
t2:1
t2:2
t2:3
可見,調用yield(),不同優先順序的線程永遠不會得到執行機會。
2.5join()
使調用join()的線程執行完畢後才能執行其它線程,在一定意義上,它可以實現同步的功能。
例16:
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicvoidrun(){
for(inti=0;i<4;i++){
System.out.println(""+i);
try{
Thread.sleep(3000);
}
catch(InterruptedExceptione){
System.out.println("Interrupted");
}
}
}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
t1.start();
try{
t1.join();
}
catch(InterruptedExceptione){}
t1.start();
}
}
運行結果為:
0
1
2
3
0
1
2
3
#p#3.classObject下常用的線程函數
wait()、notify()和notifyAll()這三個函數由java.lang.Object類提供,用於協調多個線程對共享數據的存取。
3.1wait()、notify()和notifyAll()
1)wait()函數有兩種形式:第一種形式接受一個毫秒值,用於在指定時間長度內暫停線程,使線程進入停滯狀態。第二種形式為不帶參數,代表waite()在notify()或notifyAll()之前會持續停滯。
2)當對一個對象執行notify()時,會從線程等待池中移走該任意一個線程,並把它放到鎖標志等待池中;當對一個對象執行notifyAll()時,會從線程等待池中移走所有該對象的所有線程,並把它們放到鎖標志等待池中。
3)當調用wait()後,線程會釋放掉它所佔有的「鎖標志」,從而使線程所在對象中的其它synchronized數據可被別的線程使用。
例17:
下面,我們將對例11中的例子進行修改
{
publicstaticintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
}
publicsynchronizedvoidrun(){
if(shareVar==0){
for(inti=0;i<10;i++){
shareVar++;
if(shareVar==5){
try{
this.wait();//(4)
}
catch(InterruptedExceptione){}
}
}
}
if(shareVar!=0){
System.out.print(Thread.currentThread().getName());
System.out.println("shareVar="+shareVar);
this.notify();//(5)
}
}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
TestThreadMethodt2=newTestThreadMethod("t2");
t1.start();//(1)
//t1.start();(2)
t2.start();//(3)
}}
運行結果為:
t2shareVar=5
因為t1和t2是兩個不同對象,所以線程t2調用代碼(5)不能喚起線程t1。如果去掉代碼(2)的注釋,並注釋掉代碼(3),結果為:
t1shareVar=5
t1shareVar=10
這是因為,當代碼(1)的線程執行到代碼(4)時,它進入停滯狀態,並釋放對象的鎖狀態。接著,代碼(2)的線程執行run(),由於此時shareVar值為5,所以執行列印語句並調用代碼(5)使代碼(1)的線程進入可執行狀態,然後代碼(2)的線程結束。當代碼(1)的線程重新執行後,它接著執行for()循環一直到shareVar=10,然後列印shareVar。
#p#3.2wait()、notify()和synchronized
waite()和notify()因為會對對象的「鎖標志」進行操作,所以它們必須在synchronized函數或synchronizedblock中進行調用。如果在non-synchronized函數或non-synchronizedblock中進行調用,雖然能編譯通過,但在運行時會發生IllegalMonitorStateException的異常。
例18:
{
publicintshareVar=0;
publicTestThreadMethod(Stringname){
super(name);
newNotifier(this);
}
publicsynchronizedvoidrun(){
if(shareVar==0){
for(inti=0;i<5;i++){
shareVar++;
System.out.println("i="+shareVar);
try{
System.out.println("wait......");
this.wait();
}
catch(InterruptedExceptione){}
}}
}
}
classNotifierextendsThread{
privateTestThreadMethodttm;
Notifier(TestThreadMethodt){
ttm=t;
start();
}
publicvoidrun(){
while(true){
try{
sleep(2000);
}
catch(InterruptedExceptione){}
/*1要同步的不是當前對象的做法*/
synchronized(ttm){
System.out.println("notify......");
ttm.notify();
}}
}
}
publicclassTestThread{
publicstaticvoidmain(String[]args){
TestThreadMethodt1=newTestThreadMethod("t1");
t1.start();
}
}
運行結果為:
i=1
wait......
notify......
i=2
wait......
notify......
i=3
wait......
notify......
i=4
wait......
notify......
i=5
wait......
notify......
4.wait()、notify()、notifyAll()和suspend()、resume()、sleep()的討論
4.1這兩組函數的區別
1)wait()使當前線程進入停滯狀態時,還會釋放當前線程所佔有的「鎖標志」,從而使線程對象中的synchronized資源可被對象中別的線程使用;而suspend()和sleep()使當前線程進入停滯狀態時不會釋放當前線程所佔有的「鎖標志」。
2)前一組函數必須在synchronized函數或synchronizedblock中調用,否則在運行時會產生錯誤;而後一組函數可以non-synchronized函數和synchronizedblock中調用。
4.2這兩組函數的取捨
Java2已不建議使用後一組函數。因為在調用suspend()時不會釋放當前線程所取得的「鎖標志」,這樣很容易造成「死鎖」。

⑷ java 分布式數據同步通過什麼實現的

Java是一種跨平台,適合於分布式計算環境的面向對象編程語言。

具體來說,它具有如下特性:

簡單性、面向對象、分布式、解釋型、可靠、安全、平台無關、可移植、高性能、多線程、動態性等。

下面我們將重點介紹Java語言的面向對象、平台無關、分布式、多線程、可靠和安全等特性。

1.面向對象

面向對象其實是現實世界模型的自然延伸。現實世界中任何實體都可以看作是對象。對象之間通過消息相互作用。另外,現實世界中任何實體都可歸屬於某類事物,任何對象都是某一類事物的實例。如果說傳統的過程式編程語言是以過程為中心以演算法為驅動的話,面向對象的編程語言則是以對象為中心以消息為驅動。用公式表示,過程式編程語言為:程序=演算法+數據;面向對象編程語言為:程序=對象+消息。

所有面向對象編程語言都支持三個概念:封裝、多態性和繼承,Java也不例外。現實世界中的對象均有屬性和行為,映射到計算機程序上,屬性則表示對象的數據,行為表示對象的方法(其作用是處理數據或同外界交互)。所謂封裝,就是用一個自主式框架把對象的數據和方法聯在一起形成一個整體。可以說,對象是支持封裝的手段,是封裝的基本單位。Java語言的封裝性較強,因為Java無全程變數,無主函數,在Java中絕大部分成員是對象,只有簡單的數字類型、字元類型和布爾類型除外。而對於這些類型,Java也提供了相應的對象類型以便與其他對象交互操作。

多態性就是多種表現形式,具體來說,可以用「一個對外介面,多個內在實現方法」表示。舉一個例子,計算機中的堆棧可以存儲各種格式的數據,包括整型,浮點或字元。不管存儲的是何種數據,堆棧的演算法實現是一樣的。針對不同的數據類型,編程人員不必手工選擇,只需使用統一介面名,系統可自動選擇。運算符重載(operatoroverload)一直被認為是一種優秀的多態機制體現,但由於考慮到它會使程序變得難以理解,所以Java最後還是把它取消了。

繼承是指一個對象直接使用另一對象的屬性和方法。事實上,我們遇到的很多實體都有繼承的含義。例如,若把汽車看成一個實體,它可以分成多個子實體,如:卡車、公共汽車等。這些子實體都具有汽車的特性,因此,汽車是它們的「父親」,而這些子實體則是汽車的「孩子」。Java提供給用戶一系列類(class),Java的類有層次結構,子類可以繼承父類的屬性和方法。與另外一些面向對象編程語言不同,Java只支持單一繼承。

2平台無關性

Java是平台無關的語言是指用Java寫的應用程序不用修改就可在不同的軟硬體平台上運行。平台無關有兩種:源代碼級和目標代碼級。C和C++具有一定程度的源代碼級平台無關,表明用C或C++寫的應用程序不用修改只需重新編譯就可以在不同平台上運行。

Java主要靠Java虛擬機(JVM)在目標碼級實現平台無關性。JVM是一種抽象機器,它附著在具體操作系統之上,本身具有一套虛機器指令,並有自己的棧、寄存器組等。但JVM通常是在軟體上而不是在硬體上實現。(目前,SUN系統公司已經設計實現了Java晶元,主要使用在網路計算機NC上。

另外,Java晶元的出現也會使Java更容易嵌入到家用電器中。)JVM是Java平台無關的基礎,在JVM上,有一個Java解釋器用來解釋Java編譯器編譯後的程序。Java編程人員在編寫完軟體後,通過Java編譯器將Java源程序編譯為JVM的位元組代碼。任何一台機器只要配備了Java解釋器,就可以運行這個程序,而不管這種位元組碼是在何種平台上生成的。另外,Java採用的是基於IEEE標準的數據類型。通過JVM保證數據類型的一致性,也確保了Java的平台無關性。

Java的平台無關性具有深遠意義。首先,它使得編程人員所夢寐以求的事情(開發一次軟體在任意平台上運行)變成事實,這將大大加快和促進軟體產品的開發。其次Java的平台無關性正好迎合了「網路計算機」思想。如果大量常用的應用軟體(如字處理軟體等)都用Java重新編寫,並且放在某個Internet伺服器上,那麼具有NC的用戶將不需要佔用大量空間安裝軟體,他們只需要一個

Java解釋器,每當需要使用某種應用軟體時,下載該軟體的位元組代碼即可,運行結果也可以發回伺服器。目前,已有數家公司開始使用這種新型的計算模式構築自己的企業信息系統。
3分布式

分布式包括數據分布和操作分布。數據分布是指數據可以分散在網路的不同主機上,操作分布是指把一個計算分散在不同主機上處理。

Java支持WWW客戶機/伺服器計算模式,因此,它支持這兩種分布性。對於前者,Java提供了一個叫作URL的對象,利用這個對象,你可以打開並訪問具有相同URL地址上的對象,訪問方式與訪問本地文件系統相同。對於後者,Java的applet小程序可以從伺服器下載到客戶端,即部分計算在客戶端進行,提高系統執行效率。

Java提供了一整套網路類庫,開發人員可以利用類庫進行網路程序設計,方便得實現Java的分布式特性。

4可靠性和安全性

Java最初設計目的是應用於電子類消費產品,因此要求較高的可靠性。Java雖然源於C++,但它消除了許多C++不可靠因素,可以防止許多編程錯誤。首先,Java是強類型的語言,要求顯式的方法聲明,這保證了編譯器可以發現方法調用錯誤,保證程序更加可靠;其次,Java不支持指針,這杜絕了內存的非法訪問;第三,Java的自動單元收集防止了內存丟失等動態內存分配導致的問題;第四,Java解釋器運行時實施檢查,可以發現數組和字元串訪問的越界,最後,Java提供了異常處理機制,程序員可以把一組錯誤代碼放在一個地方,這樣可以簡化錯誤處理任務便於恢復。

由於Java主要用於網路應用程序開發,因此對安全性有較高的要求。如果沒有安全保證,用戶從網路下載程序執行就非常危險。Java通過自己的安全機制防止了病毒程序的產生和下載程序對本地系統的威脅破壞。當Java位元組碼進入解釋器時,首先必須經過位元組碼校驗器的檢查,然後,Java解釋器將決定程序中類的內存布局,隨後,類裝載器負責把來自網路的類裝載到單獨的內存區域,避免應用程序之間相互干擾破壞。最後,客戶端用戶還可以限制從網路上裝載的類只能訪問某些文件系統。

上述幾種機制結合起來,使得Java成為安全的編程語言。

5多線程

線程是操作系統的一種新概念,它又被稱作輕量進程,是比傳統進程更小的可並發執行的單位。

C和C++採用單線程體系結構,而Java卻提供了多線程支持。

Java在兩方面支持多線程。一方面,Java環境本身就是多線程的。若干個系統線程運行負責必要的無用單元回收,系統維護等系統級操作;另一方面,Java語言內置多線程式控制制,可以大大簡化多線程應用程序開發。Java提供了一個類Thread,由它負責啟動運行,終止線程,並可檢查線程狀態。Java的線程還包括一組同步原語。這些原語負責對線程實行並發控制。利用Java的多線程編程介面,開發人員可以方便得寫出支持多線程的應用程序,提高程序執行效率。必須注意地是,Java的多線程支持在一定程度上受運行時支持平台的限制。例如,如果操作系統本身不支持多線程,Java的多線程特性可能就表現不出來。

希望對你有幫助!

閱讀全文

與java隊列同步相關的資料

熱點內容
雲伺服器可以攻擊嗎 瀏覽:555
主力吸籌派發區域指標源碼 瀏覽:695
單片機pc的低位元組怎麼算 瀏覽:230
pythoneval函數源碼 瀏覽:242
linuxmongodb服務啟動 瀏覽:766
在哪裡下載核酸檢測app 瀏覽:310
esxi啟動虛擬機命令 瀏覽:969
軍工級單片機 瀏覽:113
伺服器安全保護是什麼意思 瀏覽:789
刪除運行命令 瀏覽:720
龍之召喚伺服器如何 瀏覽:119
linux目錄跳轉 瀏覽:368
程序員和老闆稱兄道弟 瀏覽:759
直播網路連接源碼 瀏覽:736
用安卓手機怎麼登錄蘋果手機id 瀏覽:710
論文查重工具源碼 瀏覽:401
android銀聯demo 瀏覽:86
智能演算法發展 瀏覽:351
房車露營地用什麼app 瀏覽:70
spark編程指南python 瀏覽:553