Java多線程:
進程:進行中的程序
線程:就是進程中一個負責程序執行的控制單元(執行單元)
一個進程中可以多執行路徑,稱之為多線程
一個進程至少一個線程
開啟多個線程是為了同時運行多部分代碼
每個線程都有自己運行的內容,這個內容成為線程要執行的任務
多線程的好處:解決了多部分同時運行的問題
多線程的弊端:線程太多回到效率的降低
其實應用程序的執行都是CPU在做著快速的切換完成的,這個切換是隨機的
JVM啟動時就啟動了多個線程,至少有兩個線程
1.執行main函數的線程
該線程的任務代碼都定義在main函數里
2.負責垃圾回收的線程
如何創建一個線程:
方式一:繼承Thread類
步驟:
1.創建一個類繼承Thread類
2.重寫Thread的run方法//重寫run方法
3.直接創建Thread類的子類對象C創建線程
4.調用start方法開啟線程并調用線程的任務run方法執行
class Demo extends Thread
{
private String name = "弟鴿";
Demo(String name)
{
super(name);
//this.name = name;
}
public void run(){
for(int i=1;i<10;i++)
{
for(long j= -199999999;j<199999999;j++){ }
system.out.println(name+"*****"+i+"--------"
+Thread.currentThread().getName());
}
}
}
main(){
Demo d1 = new Demo("臠割");
Demo d2 = new Demo("兒紙");
//d1.run();
//d2.run();
d1.start();
d2.start();
}
可以通過Thread的getName獲取線程的名稱 Thread—(0開始)
主線程的名稱是 main
創建線程的目的是為了開啟一條執行路徑,去運行指定的代碼和其他代碼同時運行
而運行的指定代碼就是這個執行路徑的任務
JV創建的主線程的任務都定義在主函數里
自定義的線程的任務在run方法里;
Thread類用于描述線程,線程是 需要任務的,所以Thread類也對任務的描述,這個任務就通過Thread類中的run方法實現。也就是說,run方法就是封裝自定義線程運行任務的函數。
run方法中定義的就是線程要運行的任務代碼
開啟線程是為了運行指定代碼,所以只有繼承Thread類,并復寫run方法,將要運行的代碼定義在run方法中即可。
run()和start()的區別:
1.start()可以啟動一個新的線程
2.start()不能重復調用run()可以
3.start()中的run()代碼可以不執行完就繼續執行下面的代碼,即進行了線程切換。直接調用run()方法必須等待其代碼全部執行完才能繼續執行下面的代碼
4.start()實現了多線程,run()沒有實現多線程
臨時阻塞狀態 具備執行資格但不具備執行權 正在等待執行權
↑? ↑
↓? ? ? ? ? ? ? ? ? ? ? ↑
進程--->start()--->運行--->sleep(time)--->凍結//釋放執行權的同時
| --->? wait()? --->↑? 釋放執行資格
↓
stop()
|
↓
消亡
cpu執行資格: 可以被cpu處理,在處理隊列中排隊
cpu的執行權: 正在被cpu處理
創建線程的第二種方法:
1.定義類實現Runnable接口
2.覆蓋接口種的run方法,將線程的任務代碼封裝到run方法中
3.通過Tread類創建對象,并將Runnable接口的子類對象作為Thread類的構造函數的參數進行傳遞。(原因是線程任務都封裝在Runnable接口子類對象的run方法中,所以在線程對象創建時就得明確要運行的任務。)
4.調用線程對象的start方法開啟線程
class Demo2 implements Runnable //準備擴展Demo2類的功能,讓其中的內容作為線程的任務執行
//通過接口的形式完成
{
public void run()
{
show();
}
public void show(){
for(int i=1;i<10;i++)
{
for(long j= -199999999;j<199999999;j++){}
System.out.println(Thread.currentThread().getName());
}
}
}
main()
{
Demo2 d3 = new Demo2();
Thread t1 = new Thread(d3,"辦證");
Thread t2 = new Thread(d3,"學妹介紹Q");
t1.start();
t2.start();
}
Runnable接口:將線程的任務進行了對象的封裝
實現Runnable接口的好處:
1.將線程的任務從線程的子類中分離出來,進行了單獨的封裝
按照面向對象的思想將任務的封裝成對象
2.避免了Java單繼承的局限性
故較為常用的是實現Runnable
線程安全問題產生的原因:
1.多個線程在操作共享的數據
2.操作共享數據的代碼有多條
當一個線程在執行操作共享數據的多條代碼過程中,其他線程參與了運算
就會導致線程安全問題的產生
解決思路:
就是將多條操作共享數據的線程代碼封裝起來,當有線程在執行這些代碼的時候,必須要當成線程把這些代碼都執行完畢后,其他線程才可以? ------->局部代碼塊
在Java中用同步代碼塊就可以解決這個問題
synchronized(對象)
{
局部代碼;
}
同步的好處:
解決了線程的安全問題
同步的弊端:
相對降低了效率,因為同步外的線程都會判斷同步鎖。
同步的前提:
同步中必須有多個線程嗎,并使用同一個鎖。
同步函數的鎖是this
同步函數和同步代碼塊的區別:
1.同步函數的鎖是固定的this
同步代碼塊的鎖是任意的對象
建議使用同步代碼塊
當同步函數為static時,鎖為this.getClass()即該函數所屬字節碼文件對象,可用getClass()方法獲取,也可以用當前
類名.class 表示。
死鎖:
class DeadLockTestDemo implements Runnable
{
private boolean flag;
DeadLockTestDemo(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--If--->LockA");
synchronized(MyLock.lockB)
{
System.out.println(Thread.currentThread().getName()+"--If--->LockB");
}
}
}
else
{
while(true)
synchronized(MyLock.lockB)
{
System.out.println(Thread.currentThread().getName()+"--Else--->LockB");
synchronized(MyLock.lockA)
{
System.out.println(Thread.currentThread().getName()+"--Else--->LockA");
}
}
}
}
}
class MyLock
{
public static final Object lockA = new Object();
public static final Object lockB = new Object();
}
public class DeakLockTest {
public static void main(String[] args) {
DeadLockTestDemo dlt1 = new DeadLockTestDemo(true);
DeadLockTestDemo dlt2 = new DeadLockTestDemo(false);
Thread t1 = new Thread(dlt1);
Thread t2 = new Thread(dlt2);
t1.start();
t2.start();
}
}
進程間的通信:
等待/喚醒機制
1.wait():讓cpu處于凍結狀態,被wait的線程會被存儲到線程池中
2.notify(): 喚醒線程池中的一個線程(任意)
3.botifyAll():喚醒線程池中的所有線程
class Resource
{
String name;
int age;
boolean flag = false;
}
class Input implements Runnable
{
Resource r;
Input(Resource r)
{
this.r = r;
}
public void run()
{
int x = 0;
while(true)
{
synchronized(r)
{
if(r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(x==0)
{
r.name = "臠割";
r.age = 18;
}
else
{
r.name = "弟鴿";
r.age = 17;
}
r.flag = true;
r.notify();
}
x = (x+1)%2;
}
}
}
class Output implements Runnable
{
Resource r = new Resource();
Output(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
synchronized(r)
{
if(!r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(r.name+"--->"+r.age);
r.flag = false;
r.notify();
}
}
}
public class ResourceDemo {
public static void main(String[] args) {
Resource r = new Resource();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
線程的wait();notify();notifuAll()定義在Oblect類中的原因是:
因為這些方法是監視器的方法,監視器其實就是鎖。
鎖可以是任意的對象,任意的對象調用的方式一定定義在Object類中