進程:是一個正在執行中的程序。
每一個進程執行都有一個執行順序,該順序是一個執行路徑,或者叫一個控制單元。
線程:就是進程中的一個獨立的控制單元。線程在控制着進程的執行。
一個進程中至少有一個線程。
Java VM啓動的時候會有一個進程java.exe。該進程中至少一個線程負責java程序的執行。而且這個線程運行的代碼存在於main方法中。該線程稱爲主線程。其實更細節說明jvm,jvm啓動不止一個線程,還有負責垃圾回首機制的線程。
發現運行結果每一次都不同。因爲多個線程都獲取cpu的執行權。Cpu執行到誰,誰就運行。
除多核外,在某一個時刻,只能有一個程序在運行。Cpu在做着快速的切換,以達到看上去是同時運行的效果。我們可以形象把多線程的運行行爲在互相搶奪cpu的執行權。
這就是多線程的一個特性:隨機性。誰搶到誰執行,至於執行多長,cpu說的算。
線程有自己默認的名稱。Thread-編號,該編號從0開始。可以通過getName方法來獲取線程的名字。並且可以使用Thread的構造函數或者setName設置name參數:super(name);。static ThreadcurrentThread();獲取當前線程對象。
線程的五個狀態:
自定義線程:
通過對api的查找,java已經提供了對線程這類事物的描述。就是Thread類。
方法一創建線程:繼承Thread類。
1. 定義類繼承Thread。
2. 複寫Thread類中的run方法。(目的:將自定義代碼存儲在run方法。讓線程運行。也就是說run方法用於存儲線程要運行的代碼。)
3. 調用線程的start方法,該方法有兩個作用:啓動線程,調用run方法。(run方法和start方法的區別:start方法會開啓線程並執行該線程的run方法。Run方法僅僅是對象調用方法。而線程創建了,並沒有運行。)
public class ThreadDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Demo d1=new Demo(/*"one---"*/);
Demo d2=new Demo(/*"two---"*/);
d1.start();//開啓線程並執行該線程的run方法。
d2.start();
//d.run();僅僅是對象調用方法。而線程創建了,並沒有運行。
for (int i = 0; i < 60; i++) {
System.out.println("主線程"+i);
}
}
}
class Demo extends Thread
{
/*Demo(String name)//通過構造函數傳入線程的名字。線程會有默認的名稱Thread-編號,編號從0開始。
{
super(name);
}*/
public void run()//其中的代碼是線程中要執行的代碼。
{
for (int i = 0; i < 60; i++) {
System.out.println(this.getName()+i);//通過getName()方法獲得線程的名字。
}
}
}
方法二創建線程:實現Runable接口。步驟:
1. 定義類實現Runnable接口。
2. 覆蓋Runnable接口中的run方法。將線程要運行的代碼存放在該run方法中。
3. 通過Thread類建立線程對象。
4. 將Runnable接口的子類對戲那個作爲實際參數傳遞給Thread類的構造函數。因爲自定義的run方法所屬的對象是Runnable接口的子對象。所以要讓線程去指定指定對象的run方法。
5. 調用Thread類的start方法開啓線程並調用Runnable接口子類的run方法。
兩種方法有什麼區別呢?
繼承Thread:線程代碼存放在Thread子類run方法中。
實現Runnable:線程代碼存在接口的子類run方法中。避免了單繼承的侷限性。在定義線程時,建議使用實現方式。
public class Ticket {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TicketThread t=new TicketThread();//實現Runnable時的實現方法
Thread t1=new Thread(t);//傳入的是一個實現了Runnable的對象。
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
/*TicketThread t1=new TicketThread();
TicketThread t2=new TicketThread();
TicketThread t3=new TicketThread();
TicketThread t4=new TicketThread();//繼承Thread時的實現方法*/
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class TicketThread implements Runnable//extends Thread
{
private static int num=100;
public void run()
{
while (true)
{
if (num>=0) {
System.out.println(Thread.currentThread().getName()+"賣出1張票,餘票爲:"+num--);
}
}
}
}
多線程運行時會出現安全問題:
原因:當多條語句在操作同一個線程共享數據時,一個線程對多條語句只執行了一部分,還沒有執行完,另一個線程參與進來執行,導致共享數據的錯誤。
解決的思想:對多條操作共享數據的語句,只能讓一個線程都執行完,在執行過程中,其他線程不可以參與執行。
Java提供的解決方式:同步代碼塊。
好處:解決了多線程的安全問題。
弊端:多個線程需要判斷鎖,較爲消耗資源。
Synchronized(對象)
{
需要被同步的代碼
}
尋找需要加同步的代碼部分的方法:
1、明確那些代碼是多線程的運行代碼。
2、明確共享數據。
3、明確多線程代碼中那些語句是操作共享數據的。
這裏的對象參數如同鎖。持有鎖的現成可以在同步中執行。沒有持有鎖的線程即使獲取cpu的執行權,也進不去,因爲沒有獲取鎖。
同步的前提:
1. 必須要有兩個或者兩個以上的線程。
2. 必須是多個線程使用同一個鎖。
使用Synchronized修飾的函數爲同步函數。同步函數使用的鎖是this。如果同步函數
靜態修飾後,使用的鎖是該類對應的字節碼文件對象:類名.class。該對象的類型是class。
public class BankDemo {
/**
* @param args
*/
public static void main(String[] args)
{
Cus c=new Cus();
Thread t1=new Thread(c);
Thread t2=new Thread(c);
t1.start();
t2.start();
}
}
class Bank
{
private int sum;
//Object obj=new Object();
public synchronized void add(int n)
{
//synchronized(obj)
//{
sum=sum+n;
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println("sum="+sum);
//}
}
}
class Cus implements Runnable
{
Bank b=new Bank();
public void run()
{
for (int i = 0; i < 3; i++)
{
b.add(100);
}
}
}
單例開發模式的懶漢式:
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)//先有一個A線程進來,判斷到此處,掛起。B線程進入,也判斷,也是空,掛起。A線程繼續,創建了一個對象,結束。B線程繼續,又創建了一個對象。單例設計模式。兩個對象。
s=new Single();
}
}
return s;
}
}
懶漢式和餓漢式的區別在於延遲加載,懶漢式會出安全問題,通過同步解決,運行時程序會低效,通過雙重判斷解決。
死鎖:同步中嵌套同步會造成死鎖。
public class DeadThreadDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread t1=new Thread(new Test(true));
Thread t2=new Thread(new Test(false));
t1.start();
t2.start();
}
}
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run()
{
if (flag)
{
while (true)
{
synchronized(MyLock.locka)
{
System.out.println("if locka");
synchronized(MyLock.lockb)
{
System.out.println("if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)
{
System.out.println("else lockb");
synchronized(MyLock.locka)
{
System.out.println("else locka");
}
}
}
}
}
}
class MyLock
{
static Object locka=new Object();
static Object lockb=new Object();
}