一.java當中的線程(一)
1.1多進程和多線程
多進程:在操作系統中能同時運行多個任務(程序)。
多線程:在同一個應用程序中有多個順序流(同時)執行。其中涉及CPU時間片,調度算法。
1.2線程的執行過程
在單線程運行過程中,不涉及cpu搶佔問題。如下圖所示:
在單線程中,先執行第一條程序,再順序執行下面的程序。
多線程意味著好多個線程同時執行,但此時裡面還涉及了CPU的搶佔問題。假如先執行第一個線程的前幾步,再去執行第二個線程的前幾步,線程之間的切換速度較快。如下圖所示:
線程的執行過程:
線程在運行過程中,可能會產生阻塞事件,阻塞的原因有多種,假如線程代碼在網絡上讀取數據時,若網絡上沒有數據,則需要等待,此時線程處於阻塞狀態,不再向下運行,線程在睡眠一段時間之後,假如網絡上有這個數據,此時則解除阻塞狀態,進入就緒狀態。
1.3創建線程的方法
首先定義一個線程類,它繼承類Thread,並重寫其中的方法run(),方法run()稱為線程體;由於java只支持單繼承,用這種方法定義的類不能再繼承其他類。
public class FirstThread extends Thread{
public void run(){//權限為公共的,返回值類型為空,參數列表為空;
for(int i = 0; i<100; i++){
System.out.println("FirstThread-->"+i);
}
}
}
//程序中有三個線程在運行,一個ft,一個主線程,一個垃圾回收線程;
public class Test {
public static void main(String[] args) {
//生成線程類的對象;意味著生成了一個線程相當於線程執行過程中的new環節;
FirstThread ft = new FirstThread();
//啟動線程;start在 FirstThread中沒有出現,顧名思義就是在調用Thread方法;
ft.start();//相當於此時線程進入了就緒狀態Runnable;還涉及CPU搶佔問題,假如此時有一本小說,四個人同時想看,若有一個人在看的話,其他三人處於等待狀態;
//由於CPU搶佔的緣故,若開始執行的是主線程,那麼ft線程就處於就緒狀態,進入下面的for循環,若ft搶佔成功,此時主線程則處於就緒狀態,執行run方法中的for循環;
// ft.run();//執行run方法時,首先執行的是ft中的for循環,直至循環結束,才執行主函數中的for循環,此時就不是多線程在運行了,而是單線程在運行,因此在啟動一個線程需要時調用start方法;
for(int i =0; i<100; i++){
System.out.println("main-->"+i);
}
}
從上面的代碼中可以看出:多線程在運行時,涉及到CPU搶佔問題,因此會有線程在就緒狀態和運行狀態交替出現,線程在運行狀態時也可能因為阻塞事件,使得線程不再往下運行,只有當線程解除阻塞時才能再次運行此線程。
二.java當中的線程(二)
2.1實現線程的第二種方法
提供一個實現接口Runnable(在接口中有一個run方法)的類作為線程的目標對象,在初始化一個Thread類(將Runnable的實現類作為參數傳進去)或者Thread子類的線程對象時,把目標對象傳遞給這個線程實例,由該目標對象提供線程體。
2.2線程的簡單控制方法
中斷線程
——Thread.sleep(),其中參數單位ms,根據參數大小,來決定休眠的時間,若線程處於休眠狀態時,則不再運行,此時則讓出CPU。
此方法強制當前線程睡眠至少毫秒級別,但是使用時要對該方法捕獲,調用方法很簡單,只要在睡眠的線程中加入。
——Thread.yield()睡醒之後首先進入就緒狀態,先要進行CPU搶佔,搶佔之後才能運行。
在多線程中,為了防止某線程獨佔cpu資源,這樣其他的線程就得不到響應了,可以讓當前執行的線程“休息一下”,但是這種thread.yield()調用,並不保證下一個運行的線程就一定不是該線程。
設置線程的優先級
——setpriority()設置線程的優先級,其中最大為10,最小為1;
——getpriority()獲取線程的優先級。
public class RunnableImplement implements Runnable {
public void run(){
for(int i = 0; i<100;i++){
System.out.println("Runnable-->"+i);
if( i == 50 ){
try{
Thread.sleep(2000);//sleep存在異常,利用try...catch結構將其包裹起來來處理異常,2000ms是否能夠運行線程,取決於cpu的搶佔;
}
catch(Exception e){
System.out.println(e);
}
}
}
}
}
//儘量不使用繼承,因為在java中只支持單繼承;如果RunnableImplement繼承了Runnable以後,就不能再繼承其他類了;
public class Test1 {
public static void main(String[] args) {
//生成一個接口的實現類;
RunnableImplement ri = new RunnableImplement();
//生成一個Thread的對象,並將接口的實現類作為參數傳入Thread中;
Thread t = new Thread(ri);
//線程的最大優先級為10,最小為1;
t.setPriority(Thread.MAX_PRIORITY);
//通知Thread對象,執行start方法
t.start();
System.out.println(t.getPriority());
}
三.java當中的線程(三)
3.1多數據的數據安全
在實際開發中,使用線程程序的情況有很多,如銀行排號系統,火車站售票系統等。這種多線程的程序通常會發生問題,以火車站售票系統為例,在代碼中判斷當前票數是否大於0,如果大於0則執行將該票出售給乘客的功能,但當兩個線程同時訪問這段代碼時(假如這時只剩下一張票),第一個線程將票售出,與此同時第二個線程也已經執行完成判斷是否有票的操作,並得出結論票數大於0,於是它也進行售出操作,這樣就會產生負數。這樣會出現線程的安全問題,其問題的來源在於兩個線程同時存取單一對象的數據。
public class MyThread implements Runnable {
int i = 100 ;
public void run(){
while(true){
//Thread.currentThread(),currentThread是Thread方法的靜態方法,靜態方法的作用是獲取當前正在那個線程運行;
System.out.println(Thread.currentThread().getName() + i);
i--;
//讓出cpu,讓線程來重新搶佔cpu;
Thread.yield();
if(i<0){
break;
}
}
}
}
public class Test2 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
//生成兩個線程對象,兩個線程對象共用一個線程體;
Thread t1 = new Thread(myThread);
Thread t2 = new Thread(myThread);
//每一個線程都有名字,可以通過Thread()對象的setName()方法設置名字,也可以利用getName()獲取線程的名字;
t1.setName("線程a");
t2.setName("線程b");
//分別啟動兩個線程;
t1.start();
t2.start();
}
}
3.2線程同步機制
對於如何解決資源共享的問題呢?基本上所有解決多線程的資源衝突問題的方法都是採用給定時間只允許一個線程訪問共享資源,這時就需要給共享資源加鎖。
在Java中提供了同步機制,可以有效地防止資源衝突。同步機制使用synchronized關鍵字。
public class ThreadSafeTest implements Runnable{
int num = 10;
public void run(){
while(true){
synchronized(""){
if(num>0){
try{
Thread.sleep(1000);
}
catch(Exception e){
e.printStackTrace();
}
System.out.println("tickets"+ --num);
}
}
}
public static void main(String[] args){
ThreadSafeTest t = new ThreadSafeTest();
Thread tA = new Thread();
Thread tB= new Thread();
Thread tC = new Thread();
Thread tD = new Thread();
tA.start();
tB.start();
tC.start();
tD.start();
運行結果:打印票據未出現負數,這是因為將資源放在了同步塊中,同步塊也被稱為臨界區,它使用synchronized關鍵字建立,其語法如下:
synchronized(Object){}
通常將共享資源的操作放置在synchronized定義的區域內,這樣當其他線程也獲取到這個鎖時,必須等待鎖被釋放時才能進入該區域。Object為任意一個對象,每個對象都存在一個標誌位,並具有兩個值,分別為0和1。一個線程運行到同步塊時首先檢查該對象的標誌位,如果為0狀態,表明此同步快中存在其他線程 在運行。這時該線程處於就緒狀態,直到處於同步塊中的線程執行完同步塊中的代碼為止。這時該對象的標識位被設置為1,該線程才能執行同步塊中的線程執行完同步塊中的標識位設置為0,防止其他線程執行同步塊中的代碼。
收到此郵件是因為您加入了網易郵件列表,點擊這裡進入列表管理
如果你是該列表成員,發送主題為“查看郵件列表成員”的郵件到該列表,可以收到一封帶有列表成員的郵件。
閱讀更多 有理想的代碼dog 的文章