多線程知識梳理2
---------------------------------------------------------------------------------------------------------------------------------------------
1、實際開發 匿名內部類使用很廣泛。
2、使用 Lock 替代 synchronized 顯示加鎖解鎖 >> lock()、unlock()
3、利用 可以有多個 Condition 對象 進行指定signle 和 await
4、Condition中 await signal signalAll 替換原來Object中等待喚醒方法。
5、使用LOCK關鍵代碼:
2、使用 Lock 替代 synchronized 顯示加鎖解鎖 >> lock()、unlock()
3、利用 可以有多個 Condition 對象 進行指定signle 和 await
4、Condition中 await signal signalAll 替換原來Object中等待喚醒方法。
5、使用LOCK關鍵代碼:
private Lock lock = new ReentrantLock();private Condition conAA = lock.newCondition();private Condition conBB = lock.newCondition();
6、清除凍結狀態:interrupt() 使用時,一定要配對更改 標籤。
7、守護線程、後臺線程setDaemon(boolean) 依附於主線程,
8、臨時加入線程 join() 能夠獲取執行權,被剝奪執行權的線程只有在該線程結束後纔會參加搶奪
7、守護線程、後臺線程setDaemon(boolean) 依附於主線程,
8、臨時加入線程 join() 能夠獲取執行權,被剝奪執行權的線程只有在該線程結束後纔會參加搶奪
執行join() 方法前的線程會和當前線程搶奪資源
9、線程組、優先級 setPriority設定優先級 記住MAX、MIN、MOR、NORM
10、暫停 線程 yield() 臨時的釋放執行權,減緩線程的運行頻率,保證每個線程能較爲平均的運行
-------
11、同步代碼的鎖,最好的是該類中 最唯一的對象 r 最具唯一性
12、共享數據 都必須同步 synchronized(r) 2處都是共享數據
13、通過一個 flag標記,來判斷,一個線程是否結束, 通過 wait 等待 和 notify 喚醒機制,
14、wait() notify() notifyAll 必須同一把鎖 ,鎖可以是任意的對象,而被任意對象調用的方法定義在Object中
15、notify() 適合僅僅2個線程的時候。當大於2個線程的時候,由於喚醒機制,容易出現安全問題。
16、當同時有多個線程,執行相同功能時候,必須寫 while 循環判斷標記 使用 notifyAll 喚醒機制
-------------------------------------------------------
10、暫停 線程 yield() 臨時的釋放執行權,減緩線程的運行頻率,保證每個線程能較爲平均的運行
-------
11、同步代碼的鎖,最好的是該類中 最唯一的對象 r 最具唯一性
12、共享數據 都必須同步 synchronized(r) 2處都是共享數據
13、通過一個 flag標記,來判斷,一個線程是否結束, 通過 wait 等待 和 notify 喚醒機制,
14、wait() notify() notifyAll 必須同一把鎖 ,鎖可以是任意的對象,而被任意對象調用的方法定義在Object中
15、notify() 適合僅僅2個線程的時候。當大於2個線程的時候,由於喚醒機制,容易出現安全問題。
16、當同時有多個線程,執行相同功能時候,必須寫 while 循環判斷標記 使用 notifyAll 喚醒機制
-------------------------------------------------------
【1】
多線程中存在的安全問題
//例子1--2個線程。
多線程中存在的安全問題
//例子1--2個線程。
class names2
{
public static void main(String[] args) {
Res r = new Res();
new Thread(new input(r)).start();
new Thread(new output(r)).start();
}
}
class Res{
private String name;
private String sex;
boolean flag;
public synchronized void set(String name,String sex){
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
notify();
}
public synchronized void out(){
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+">>"+sex);
flag = false;
notify();
}
}
class input implements Runnable
{
private Res r;
input(Res r){
this.r = r;
}
public void run(){
int x = 0 ;
while(true){
if(x == 0)
r.set("lili","11111111");
else
r.set("達到","-----------");
x = (x+1)%2;
}
}
}
class output implements Runnable{
private Res r;
private boolean flag;
output(Res r){
this.r = r;
}
public void run(){
while(true){
r.out();
}
}
}
生產和消費 4個線程 使用上訴邏輯 顯然是不行的。class shangping {
public static void main(String[] args) {
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
class Resource{
private String name;
private int num=1;
private boolean flag;
public synchronized void set(String name){
while(flag)// 循環致使每一次都會判斷
try{this.wait();}catch(Exception e){}
this.name = name+".."+num++;
System.out.println(Thread.currentThread().getName()+">>>"+this.name);
this.flag = true;
this.notifyAll();//喚醒全部線程,避免只喚醒本方線程
}
public synchronized void out(){
while(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"================="+this.name);
this.flag = false;
this.notifyAll();
}
}
//sheng chan
class Producer implements Runnable{
private Resource r ;
Producer(Resource r ){
this.r = r ;
}
public void run(){
while(true){
r.set("CPU");
}
}
}
class Consumer implements Runnable{
private Resource r ;
Consumer(Resource r ){
this.r = r ;
}
public void run(){
while(true){
r.out();
}
}
}
例子2是對例子1進行了線程的擴編, 發現 notify侷限性,1、只會判斷標記一次,如果改爲while又會出現全部臥倒的可能性。
可以看出,多線程的安全問題是一個需求十分嚴謹卻又痛苦萬分的。然後來看看更新優化
【2】
【2-1】1.5新特性:
將同步 synchronized 替換成 顯示 的 Lock
將Objcet 中替換成Condition 對象 wait notify notifyAll >>>>await signal signalAll
似乎沒有太大的更新,
但是 Lock 中可以有 多個 Condition 對象
也就是說,在讀取標籤的時候,可以指定讓持有具體鎖的那個對象 await 或者 signal
這樣一樣,代碼就明朗了很多,不會出現只喚醒一方的情況。有效的避免了 全部臥倒。
替換後的代碼。其實思路是一樣了,不同的就是把加鎖,釋放鎖的過程放在了名面上。
class Resource{
private String name;
private int num=1;
private boolean flag;
private Lock lock = new ReentrantLock();
private Condition conAA = lock.newCondition();
private Condition conBB = lock.newCondition();
public void set(String name){
lock.lock();
try{
while(flag)
try{conAA.await();}catch(InterruptedException e){}//true>>讓A方都await
this.name = name+".."+num++;
System.out.println(Thread.currentThread().getName()+">>>"+this.name);
flag = true;
conBB.signalAll();// A方完事了>>>flag = true>>讓B方 醒
}
finally{
lock.unlock();
}
}
public void out(){
lock.lock();
try {
while(!flag)
try{conBB.await();}catch(InterruptedException e){}// false>> 讓B都await
System.out.println(Thread.currentThread().getName()+"================="+this.name);
flag = false;
conAA.signalAll();// B 方完事了>>>flag = false>> 讓A 方醒
}
finally{
lock.unlock();
}
}
}
InterruptedException : 當一個線程在運行中,被中斷的異常。多線程的運行,通常都是循環結構,控制住 循環,就可以讓run方法結束,也就結束了線程。
特殊情況:
當線程處於凍結狀態,就不會讀取標記,那麼線程就不會結束
當沒有指定的方式讓凍結的線程恢復到運行狀態,這時需要對凍結狀態進行清除
強制讓線程恢復到運行狀態中來,這樣就可以操作標記讓線程結束。
【2-2】
Thread 提供方法: interrupt() 清除凍結狀態
使用時,1,interrupt清除,2改變標籤值。
class stop implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");
flag = false;//【2】清除凍結狀態後 改變標籤屬性 達到結束目的。
}
System.out.println(Thread.currentThread().getName()+">>>>RUN");
}
}
public void changeFlag(){
flag = false;
}
}
public class interrupt {
public static void main(String[] args) {
stop s = new stop();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.start();
int num = 0 ;
while(true){
if(num++ ==60){
//s.changeFlag();
t1.interrupt();//【1】 清除 凍結狀態
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"MAIN------------");
}
System.out.println("over");
}
}
【2-3】void setDaemon(boolean)
設置爲守護線程或用戶線程(後臺線程)前臺線程一結束,後臺線程都結束
必須設定在 start 前。
上面例子部分修改一下
t1.setDaemon(true);//設定爲守護線程
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0 ;
while(true){
if(num++ ==60){
//t1.interrupt();//【1】 清除 凍結狀態
break;
}
System.out.println(Thread.currentThread().getName()+"MAIN------------");
}
當沒有 設定 守護線程 的時候,會出現等待現象,而,當設定了後臺後, 主線程 一結束,t1 t2 也跟着結束了。
註釋掉 try 部分, 觀看是否設定 後臺線程 結果更爲明顯。
while(flag){
/* try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");
flag = false;//【2】清除凍結狀態後 改變標籤屬性 達到結束目的。
}
*/ System.out.println(Thread.currentThread().getName()+">>>>RUN");
}
【2-4】join() 臨時加入線程執行。
當進行多線程運算時,不論誰擁有執行權(假設B),當加入 A.join()
當讀到A.join() 表示A線程要執行權,A 要優先執行。
A 線程結束後,才釋放執行權,這個時候B 纔會參與和他線程再相互搶奪執行權利
也就說說,A.join() 前只有A時, A執行
A.join() 前有A和C時, AC相互搶奪 執行權
最開始拿到執行權利的B 只能等待A 執行完畢後,纔會參與權利搶奪。
public class jion {
public static void main(String[] args) {
jionD j = new jionD();
Thread t1 = new Thread(j);
Thread t2 = new Thread(j);
t1.start();
//t2.start();//當 t2在t1.join() 前時候,會和 t1 搶奪執行權。
try {t1.join();} catch (InterruptedException e) {}
t2.start();
for(int i = 0 ;i <70 ; i++){
System.out.println(Thread.currentThread().getName()+"-------"+i);
}
}
}
class jionD implements Runnable{
public void run() {
for(int i = 0 ;i< 70 ; i++){
System.out.println(Thread.currentThread().getName()+">>"+i);
}
}
}
總之一句話,join() 會剝奪擁有當前執行權的線程,一直到join() 對應的線程結束纔會參與搶奪,至於 join() 對應的線程會不會被其他線程搶奪資源另說。【2-5-1】
setPriority() 設定優先級
通過 Thread.currentThread().toString() 查看 到線程的優先級和線程組
ThreadGroup 線程組對象 據說用的很少
創建線程組,把線程裝入該對象,即是一個線程組
優先級 :1-10級 默認優先級 5
MAX_PRIORITY >>10
MIN_PRIORITY >>5
NORM_PRIORITY >>1
根據上面的例子
t2.setPriority(MAX_PRIORITY);
這個時候t2 線程的優先級最高,在運行越多時越明顯,但不代表 ,其他線程搶奪不到資源。
【2-5-2】
yield () 方法
暫停當前正在執行的線程對象,並執行其他的線程。
class jionD implements Runnable{
public void run() {
for(int i = 0 ;i< 70 ; i++){
System.out.println(Thread.currentThread().getName()+">>"+i);
Thread.yield();//上列代碼 修改部分
}
}
}
這個時候,t1 t2不論誰進來,會稍微減少線程運行頻率,並保證每一個線程都有機會平均性的運行。【3】
老師給出的,實際開發中 常用的 線程方式, 感覺 匿名內部類的使用是非常普遍的。
public class ThreadTest {
public static void main(String[] args) {
//
new Thread()
{
public void run(){
for(int i = 0 ;i < 100 ; i++ ){
System.out.println("匿名內部類線程");
}
}
}.start();
for(int i = 0 ;i < 100 ; i++ ){
System.out.println("主線程部分");
}
//
Runnable r = new Runnable()
{
public void run(){
for(int i = 0 ;i < 100 ; i++ ){
System.out.println("不同封裝方式達到同時運行的效果");
}
}
};
new Thread(r).start();
}
}
//end
---------------------------------------------------------------------------------------------------------------------------------------------
----------
android培訓、 java培訓、期待與您交流!----------