標籤:
主要內容
1.理解線程的並發性
2.線程的同步
3.線程的常用方法
上一章中由於線程的並發性導致了多線程的執行總是會出現一些問題。。線程的並發性是程式員不可控制
的,也是不可避免的,線程的並發性往往會導致問題的出現。。那麼我們為什麼要控制線程的並發性呢?比
如說:一個公子管理負責人正在修改僱員的工資表,而一些僱員正在領取工資,如果允許這樣做必然會造成
混亂,因此,工資管理負責人正在修改工資表的時候,不允許任何的僱員領取工資,也就是說僱員們必須執行
等待狀態。。
public class tickets { public static void main(String[] args) { sel s=new sel(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.start(); t2.start(); t3.start(); }}class sel implements Runnable{ private int num=100; public void run() { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(num>0) { System.out.println(Thread.currentThread().getName()+"正在出售第"+num+"張票"+num--); }else { break; } } }}
依然是賣票問題,上述的線程由於線程的並發性而導致了賣票視窗出現了問題。。
為瞭解決這一問題,我們引入線程的同步。。
2.線程的同步
何為同步:我的理解就是:上述有三個線程,當t1線程啟動的時候調用run()方法,那麼同步函數也就起了作用,當t1執行run()
方法時,CPU分配給t1線程一把鑰匙(並且只有一把),拿到這個鑰匙之後開啟synchronized函數的鎖,然後進去執行裡面的方法,
同時拿著鑰匙,然後把門鎖上,其他線程想訪問這個函數的時候CPU沒有鑰匙,鑰匙被線程t1拿進去了,所以其他的線程只能線上程池
中進行排隊等待,當t1執行完畢後,把門開啟,然後將鑰匙還給CPU,然後t1線程或者消亡,或者進入阻塞狀態,或者執行其他的過程
然後CPU再次把鑰匙給線上程池中排隊等待的線程,依次類推。。
(1)synchronized 同步函數
(2)synchronized 同步代碼塊
同步函數:
public class tickets { public static void main(String[] args) { sel s=new sel(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.start(); t2.start(); t3.start(); }}class sel implements Runnable{ private int num=100; public synchronized void run()//同步函數的建立 { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(num>0) { System.out.println(Thread.currentThread().getName()+"正在出售第"+num+"張票"+num--); }else { break; } } }}
那麼上面賣票中出現的問題就得到瞭解決,就不會出現一張票被賣多次,或者是出現賣第0張票的可能了。。
同步代碼塊:
public class tickets { public static void main(String[] args) { sel s=new sel(); Thread t1=new Thread(s); Thread t2=new Thread(s); Thread t3=new Thread(s); t1.start(); t2.start(); t3.start(); }}class sel implements Runnable{ private int num=100; public void run()//同步代碼塊的建立 { synchronized(this) { while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if(num>0) { System.out.println(Thread.currentThread().getName()+"正在出售第"+num+"張票"+num--); }else { break; } } } }}
同步代碼塊的實現也可以避免線程的並發性,並且同步代碼塊比同步函數更具有優勢,同步代碼塊的
效率也是比同步函數要高的。最重要的是同步代碼塊的好處在於:
同步函數的鎖只能是this,而同步代碼塊可以是任意一個obj對象,可以定義自己的類來定義自己的鎖
同步代碼塊的速度也是比同步函數要更加快一些,應用的更加的廣泛。。因此,在處理多安全執行緒問題的時候
最好使用同步代碼塊。。
線程的常用方法
1.start() 2.run() 3.sleep()4.isAlive()5.currentThread()6.interrupt()
前面的三個就不在進行介紹了。想必學習到了線程就知道這三個方法是什麼意思了,isAlive()方法是判斷
當前線程是否還活著。。currentThread()方法是擷取當前執行線程的對象名,interrupt()方法是喚醒
休眠的線程。。下面舉個例子。。。
/* * 比如說張小帥正在睡覺,那麼正是上課時間 * 張小帥睡覺線程正在執行,但是在上課時的 * 老師這個線程看到了張小帥睡覺線程,然後 * 直接叫醒了張小帥線程。。。 * */public class Demo_1_1 { public static void main(String[] args) { ClassRoom room=new ClassRoom(); room.students.start(); room.teacher.start(); }}class ClassRoom implements Runnable{ Thread students,teacher; ClassRoom() { teacher=new Thread(this); students=new Thread(this); teacher.setName("張老師"); students.setName("張小帥"); } public void run() { if(Thread.currentThread()==students) { System.out.println(Thread.currentThread().getName()+"正在睡覺,不聽課"); try { Thread.sleep(1000*60*60); } catch (InterruptedException e) { System.out.println(students.getName()+"被喚醒"); } System.out.println("開始聽課"); } else if(Thread.currentThread()==teacher) { for(int i=1;i<=3;i++) { System.out.println("上課"); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } students.interrupt(); } }}
順便提一嘴。。一個已經啟動並執行線程在沒有進入死亡狀態時,不要再次分配實體,由於線程的分配只能引用
最後一個實體,那麼先前的實體就會成為垃圾,並且記憶體回收站不會去進行回收,因為JVM會認為那個垃圾
實體正在運行,如果突然使其中斷,那麼必然會導致裝置的損壞。。。
比如說:
Thread thread=new Thread(target);
thread.start();
如果再次執行下面的語句。。。
thread=new Thread(target);那麼原本的實體就會成為垃圾。。。
就好比下面這樣。。。。
因此在寫多線程的時候一定要避免這樣的問題發生。。。
JAVA 多線程機制(二)