一線程的概念
進程:是一個正在執行中的程式,每一個進程執行都有一個執行順序。該順序是一個執行路徑,或者叫一個控制單元。
線程:就是進程中的一個獨立的控制單元;線程在控制著進程的執行。
Java虛擬機器啟動的時候會有一個進程java.exe.該進程中至少一個線程負責java程式的執行。而且這個線程啟動並執行代碼存在於main方法中,該線程稱之為主線程,每個進程中至少有一個線程。
二線程的建立
第一種方式:建立線程繼承Thread類,
步驟:
1,定義類繼承Thread。
2,複寫Thread類中的run方法。
目的:定義自己的任務讓線程驅動。
3,調用線程的start方法,該方法兩個作用:啟動線程,調用run方法。
如下代碼:
class MyThread extends Thread
{
public void run()
{
for(int i=0; i<100; i++)
System.out.println("Thread1 run----"+x);
}
}
class MyThreadDemo
{
public static void main(String[] args)
{
//for(int x=0; x<4000; x++)
//System.out.println("Hello World!");
MyThread t = new MyThread();//建立好一個線程。
//t.start();//開啟線程並執行該線程的run方法。
t.run();//僅僅是對象調用方法。而線程建立了,並沒有運行。
for(int i=0; i<60; i++)
System.out.println("Hello World!--"+i);
}
}
實現Runnable介面,實現介面裡的run方法
步驟:
1,設計類實現Runnable介面實現該介面裡的run方法;
2,建立該類的是兩年作為參數傳如Thread類的構造方法;
如下代碼:
class MyThread implements Runnable{
public void run(){
//這裡定義自己的任務
}
}
然後在建立啟動線程
new(new MyThread()).start();
採用繼承Thread類方式:
(1)優點:編寫簡單,如果需要訪問當前線程,無需使用Thread.currentThread()方法,直接使用this,即可獲得當前線程。
(2)缺點:因為線程類已經繼承了Thread類,所以不能再繼承其他的父類。
採用實現Runnable介面方式:
(1)優點:線程類只是實現了Runable介面,還可以繼承其他的類。在這種方式下,可以多個線程共用同一個目標對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU代碼和資料分開,形成清晰的模型,較好地體現了物件導向的思想。
(2)缺點:編程稍微複雜,如果需要訪問當前線程,必須使用Thread.currentThread()方法。
對象如同鎖。持有鎖的線程可以在同步中執行。
沒有持有鎖的線程即使擷取cpu的執行權,也進不去,因為沒有擷取鎖。
最後總結一下單例設計模式:
懶漢設計模式如下:
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
//--標記A
s = new Single() ;
}
}
return s;
}
采懶漢設計模式時候存在安全隱患,當線程執行到A處的時候執行權被剝奪了停了了下來此時另一個線程就可能執行到此處這樣就可能建立多個Single對象引發錯誤;這種設計必須同步處理如下代碼:
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}
另外一種設計模式是餓漢式這種設計安全性比較高
class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
四 線程的安全問題的討論與總結
同步的前提:
1,必須要有兩個或者兩個以上的線程。
2,必須是多個線程使用同一個鎖。
必須保證同步中只能有一個線程在運行。
好處:解決了多線程的安全問題。
弊端:多個線程需要判斷鎖,較為消耗資源
加鎖對象是Object的執行個體;也可以通過加鎖函數來實現同步此時的加鎖對象是調用該函數的this,靜態同步方法,使用的鎖是該方法所在類的位元組碼檔案對象。類名.class,如下代碼:
多線程編程時候,常常出現錯誤的情況,這是由於系統線程調度有一定的隨機性,所以我們必須對臨界資源進行同步,下面是一個經典的例子銀行取錢問題
取錢的步驟:
1,使用者輸入帳號密碼系統判斷使用者的賬戶密碼是否匹配。
2,使用者輸入取款金額。
3,系統判斷賬戶餘額是否大於取款金額。
4,如果大於就成功否則就失敗。
我們按上面的流程去編寫取款程式,我們用兩條線程來類比取錢操作:
public class Account
{
//封裝賬戶編號、賬戶餘額兩個屬性
private String accountNo;
private double balance;
public Account(){}
//構造器
public Account(String accountNo , double balance)
{
this.accountNo = accountNo;
this.balance = balance;
}
public void setAccountNo(String accountNo)
{
this.accountNo = accountNo;
}
public String getAccountNo()
{
return this.accountNo;
}
public void setBalance(double balance)
{
this.balance = balance;
}
public double getBalance()
{
return this.balance;
}
//下面兩個方法根據accountNo來計算Account的hashCode和判斷equals
public int hashCode()
{
return accountNo.hashCode();
}
public boolean equals(Object obj)
{
if (obj != null
&& obj.getClass() == Account.class)
{
Account target = (Account)obj;
return target.getAccountNo().equals(accountNo);
}
return false;
}
}
接下來提供一個取錢的線程類,執行賬戶,取錢數量進行操作,邏輯是餘額不足時候無法提取現金,當餘額足夠時候吐出鈔票,餘額減少。
public class DrawThread extends Thread
{
//類比使用者賬戶
private Account account;
//當前取錢線程所希望取的錢數
private double drawAmount;
public DrawThread(String name , Account account ,
double drawAmount)
{
super(name);
this.account = account;
this.drawAmount = drawAmount;
}
//當多條線程修改同一個共用資料時,將涉及到資料安全問題。
public void run()
{
//賬戶餘額大於取錢數目
if (account.getBalance() >= drawAmount)