?? day12.txt
字號:
多線程:
多線程的同步:
多線程并發訪問同一個對象(臨界資源),如果不對線程進行同步控制,破壞了原子操作(不可再分的操作),則會造成臨界資源(兩個線程同時訪問的資源)的數據不一致。
每一個對象都有一個互斥的鎖標記和一個鎖池。當線程擁有這個對象的鎖標記時才能訪問這個資源,沒有鎖標記便進入鎖池,保證在同步代碼塊中只有一個線程,解決了多線程同步控制的問題。
關鍵字:synchronized //線程在同步代碼中必須采用串行訪問
synchronized修飾代碼塊:對括號內的對象object加鎖,只有拿到對象鎖標記的線程才能進入該代碼塊。
public void push(char c){
synchronized(object){ //object只要是對象就可以,但必須保證是同一對象
……
同步代碼
……
}
}
synchronized修飾方法:在整個方法范圍內對當前對象的加鎖,只有拿到對象鎖標記的線程才能執行該方法。盡可能的少用
public synchronized void push(char c) {
……
同步代碼
……
}
一個線程可以同時擁有多個對象的鎖標記,鎖標記如果過多,就會出現線程等待其他線程釋放鎖標記,而又都不釋放自己的鎖標記供其他線程運行的狀況,造成死鎖。
靜態方法可以是同步方法:但是它所鎖的并不是當前對象,是類對象。
抽象方法不能是synchronized同步的方法。
構造方法不能是synchronized同步的方法。
線程因為未拿到鎖標記而發生阻塞進入鎖池(lock pool)。每個對象都有自己的一個鎖池的空間,用于放置等待運行的線程。由系統決定哪個線程拿到鎖標記并運行
利用Collections類中的synchronizedXxxx(Xxxx ss)方法可以得到相應集合的線程安全的集合
注意:
在同步語句塊中不能直接操作對象鎖正在使用的對象。
對象與鎖一一對應。
同步依賴對象鎖,鎖對象相同,同步語句串行,鎖對象不同,同步語句并行。
順序鎖,不要回調,反向打開。
能不用同步就不用同步,有數據共享沖突時才使用同步。
等待通知機制:
線程間通信使用的空間稱之為對象的等待對列(wait pool),該隊列也是屬于對象的空間的。
使用Object類中wait()的方法,在運行狀態中,線程調用wait(),此時表示線程將釋放自己所有的鎖標記和CPU的占用,同時進入這個對象的等待池。等待池的狀態也是阻塞狀態,只不過線程釋放自己的鎖標記。只有在對該對象加鎖的同步代碼塊里,才能掉用該對象的wait(),表示線程將會釋放所有鎖標記,進入等待隊列,線程將進入等待隊列狀態。
一個線程進入了一個對對象加鎖的同步代碼塊,并對該對象調用了wait()方法,釋放自己擁有的所有鎖標記,進入該對象等待隊列,另一個線程獲得了該對象的鎖標記,進入代碼塊對該對象調用了notify()方法,就會從等待隊列里釋放出一線程,釋放出的這個線程要繼續運行就還要進入那個同步代碼塊,因為得不到要訪問代碼塊對象的鎖標記,而進入該對象的鎖池,等待鎖標記釋放。
什么情況下釋放鎖:
同類代碼執行完畢。
異常未處理,錯誤退出。
調用wait()。
相關方法:
1) wait():交出鎖和CPU的占用;
2) notify():將從對象的等待池中移走一個任意的線程,并放到鎖池中,那里的對象一直在等待,直到可以獲得對象的鎖標記。
3) notifyAll(): 將從等待池中移走所有等待那個對象的線程并放到鎖池中,只有鎖池中的線程能獲取對象的鎖標記,鎖標記允許線程從上次因調用wait()而中斷的地方開始繼續運行
注意:
用notifyAll()取代notify(),因為在調用notify()方法時,是由系統決定釋放出哪個線程。
只能對加鎖的資源進行wait()和notify()。
判斷是否進行等待wait()時,用while代替if來進行判斷。
I/O流
字節輸入流:InputStream類為所有字節輸入流的父類
三個基本的read()方法:
int read()
從流里讀出的一個字節。不推薦使用
int read(byte[] b)
將數據讀入到字節數組中,并返回所讀的字節數
int read(byte[] b, int off, int len)
off 從哪里開始讀。
len 讀取多少。
將輸入流中最多 len 個數據字節讀入字節數組。
其它方法:
void close()
關閉此輸入流并釋放與該流關聯的所有系統資源。
int available()
返回不受阻塞地從此輸入流讀取的字節數。
long skip(long n)
跳過和放棄此輸入流中的n個數據字節,該方法有可能失效。
boolean markSupported()
測試此輸入流是否支持 mark 和 reset 方法。
void mark(int n)
在此輸入流中標記當前的位置
void reset()
將此流重新定位到對此輸入流最后調用 mark 方法時的位置。
字節輸出流:OutputStream類是所有字節輸入流的父類
三個基本的write()方法:
void write(int n)
將指定的字節寫入此輸出流。
void write(byte[] b)
將 b.length 個字節從指定的字節數組寫入此輸出流。
void write(byte[] b, int off, int len)
將指定字節數組中從偏移量off開始的len個字節寫入此輸出流。
其它方法:
void close()
關閉此輸出流并釋放與此流有關的所有系統資源。
void flush()
刷新此輸出流并強制寫出所有緩沖的輸出字節。
文件輸入輸出流:FileInputStream和FileOutputStream
要構造一個FileInputStream,所關聯的文件必須存在而且是可讀的。
如:
FileInputStream fis = new FileInputStream("myfile.dat");
要構造一個FileOutputStream,而輸出文件已經存在,則它將被覆蓋。
如:
FIleOutputStream fos = new FileOutputStream("results.dat");
要想以追加的方式寫,則需要一個額外的參數,如:
FileOutputStream outfile = new FileOutputStream("results.dat" ,true); //參數為true時輸出為追加,為false時為覆蓋。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -