JAVA多執行緒
多執行緒的基本概念
執行緒指程式中的一個執行場景,也就是執行流程,那麼程式和執行緒有什麼區別呢?
• 每個程式是一個應用程式,都有獨立的記憶體空間
• 同一個程式中的執行緒共享其程式中的記憶體和資源(共享的記憶體是堆記憶體和方法區記憶體,棧記憶體不共享,每個執行緒有自己的。)
什麼是程式?
一個程式對應一個應用程式。例如:在windows作業系統啟動Word就表示啟動了一個
程式。在java的開發環境下啟動JVM,就表示啟動了一個程式。現代的計算機都是支援多
程式的,在同一個作業系統中,可以同時啟動多個程式。
多程式有什麼作用?
單程式計算機只能做一件事情。
玩電腦,一邊玩遊戲(遊戲程式)一邊聽音樂(音樂程式)。
對於單核計算機來講,在同一個時間點上,遊戲程式和音樂程式是同時在執行嗎?不是。
因為計算機的CPU只能在某個時間點上做一件事。由於計算機將在“遊戲程式”和“音樂
程式”之間頻繁的切換執行,切換速度極高,人類感覺遊戲和音樂在同時進行。
多程式的作用不是提高執行速度,而是提高CPU的使用率。
程式和程式之間的記憶體是獨立的。
什麼是執行緒?
執行緒是一個程式中的執行場景。一個程式可以啟動多個執行緒。
多執行緒有什麼作用?
多執行緒不是為了提高執行速度,而是提高應用程式的使用率。
執行緒和執行緒共享“堆記憶體和方法區記憶體”,棧記憶體是獨立的,一個執行緒一個棧。
可以給現實世界中的人類一種錯覺:感覺多個執行緒在同時併發執行。
java程式的執行原理?
java命令會啟動java虛擬機器,啟動JVM,等於啟動了一個應用程式,表示啟動了一個程式。該程式會自動啟動一個“主執行緒”,然後主執行緒去呼叫某個類的main方法。所以main方法執行在主執行緒中。在此之前的所有程式都是單執行緒的。
執行緒生命週期
執行緒是一個程式中的執行場景,一個程式可以啟動多個執行緒
新建:採用new語句建立完成就緒:執行start後
執行:佔用CPU時間
阻塞:執行了wait語句、執行了sleep語句和等待某個物件鎖,等待輸入的場合
終止:退出run()方法
多執行緒不是為了提高執行速度,而是提高應用程式的使用率.
執行緒和執行緒共享”堆記憶體和方法區記憶體”.棧記憶體是獨立的,一個執行緒一個棧.
可以給現實世界中的人類一種錯覺:感覺多執行緒在同時併發執行.
很多人都對其中的一些概念不夠明確,如同步、併發等等,讓我們先建立一個資料字典,以免產生誤會。
• 多執行緒:指的是這個程式(一個程式)執行時產生了不止一個執行緒
• 並行與併發:
•
o 並行:多個cpu例項或者多臺機器同時執行一段處理邏輯,是真正的同時。
•
o 併發:通過cpu排程演算法,讓使用者看上去同時執行,實際上從cpu操作層面不是真正的同時。併發往往在場景中有公用的資源,那麼針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。
執行緒安全:經常用來描繪一段程式碼。指在併發的情況之下,該程式碼經過多執行緒使用,執行緒的排程順序不影響任何結果。這個時候使用多執行緒,我們只需要關注系統的記憶體,cpu是不是夠用即可。反過來,執行緒不安全就意味著執行緒的排程順序會影響最終結果,如不加事務的轉賬程式碼:
voidtransferMoney(Userfrom,Userto,floatamount){
to.setMoney(to.getBalance()+amount);
from.setMoney(from.getBalance()-amount);
}
同步:Java中的同步指的是通過人為的控制和排程,保證共享資源的多執行緒訪問成為執行緒安全,來保證結果的準確。如上面的程式碼簡單加入@synchronized關鍵字。在保證結果準確的同時,提高效能,才是優秀的程式。執行緒安全的優先順序高於效能。
Java命令會啟動Java虛擬機器,啟動JVM,等於啟動了一個應用程式,表示啟動了一個程式,該程式會自動啟動一個”主執行緒”,
然後主執行緒去呼叫某個類的main()方法,所以main()方法執行在主執行緒中.
執行緒的排程與控制執行緒的排程模型分為:分時排程模型和搶佔式排程模型,Java使用搶佔式排程模型
通常我們的計算機只有一個CPU,CPU在某一個時刻只能執行一條指令,執行緒只有得到CPU時間片,也就是使用權,才可以執行指令。在單CPU的機器上執行緒不是並行執行的,只有在多個CPU上執行緒才可以並行執行。Java虛擬機器要負責執行緒的排程,取得CPU的使用權,目前有兩種排程模型:分時排程模型和搶佔式排程模型,Java使用搶佔式排程模型。分時排程模型:所有執行緒輪流使用CPU的使用權,平均分配每個執行緒佔用CPU的時間片搶佔式排程模型:優先讓優先順序高的執行緒使用CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個,優先順序高的執行緒獲取的CPU時間片相對多一些。
分時排程模型:所有執行緒輪流使用CPU的使用權,平均分配每個執行緒佔用CPU的時間片
搶佔式排程模型:優先讓優先順序高的執行緒使用CPU,如果執行緒的優先順序相同,那麼會隨機選擇一個,優先順序高的執行緒獲取的CPU時間片相對多一些.
publicclassThreadTest{
publicstaticvoidmain(String[]args){
ThreadTest1();
//ThreadTest2();
//ThreadTest3();
//ThreadTest4();
//ThreadTest5();
}
/**
*三個方法:獲取當前執行緒物件:Thread.currentThread();給執行緒起名:t1.setName("t1");獲取執行緒的名字:
*t.getName();
*/
privatestaticvoidThreadTest1(){
Threadt=Thread.currentThread();//t儲存的記憶體地址指向的執行緒為"主執行緒"
System.out.println(t.getId());
Threadt1=newThread(newProcessor1());
//給執行緒起名
t1.setName("t1");
t1.start();
Threadt2=newThread(newProcessor1());
t2.setName("t2");
t2.start();
}
/**
*執行緒優先順序高的獲取的CPU時間片相對多一些優先順序:1-10最低:1最高:10預設:5
*/
privatestaticvoidThreadTest2(){
Threadt1=newProcessor2();
Threadt2=newProcessor2();
t1.setName("t1");
t2.setName("t2");
System.out.println(t1.getPriority());
System.out.println(t2.getPriority());
t1.setPriority(1);
t2.setPriority(10);
t1.start();
t2.start();
}
/**
*1.Thread.sleep(毫秒);2.sleep方法是一個靜態方法3.該方法的作用:阻塞當前執行緒,騰出CPU,讓給其它執行緒
*/
privatestaticvoidThreadTest3(){
Threadt=newThread(newProcessor3());
t.start();
for(inti=0;i<11;i++){
System.out.println(Thread.currentThread().getName()+"========>"
+i);
try{
t.sleep(5000);//等同於Thread.sleep(5000);阻塞的還是當前執行緒,和t執行緒無關.
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
/**
*某執行緒正在休眠,如何打斷它的休眠以下方式依靠的是異常處理機制
*/
privatestaticvoidThreadTest4(){
try{
Threadt=newThread(newProcessor4());
t.start();
Thread.sleep(5000);//睡5s
t.interrupt();//打斷Thread的睡眠
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
/**
*如何正確的更好的終止一個正在執行的執行緒需求:執行緒啟動5s之後終止.
*/
privatestaticvoidThreadTest5(){
Processor5p=newProcessor5();
Threadt=newThread(p);
t.start();
//5s之後終止
try{
Thread.sleep(5000);
p.isRun=false;
}catch(InterruptedExceptione){
//TODOAuto-generatedcatchblock
e.printStackTrace();
}
}
}
classProcessor1implementsRunnable{
@Override
publicvoidrun(){
Threadt=Thread.currentThread();//t儲存的記憶體地址指向的執行緒為"t1執行緒物件"
System.out.println(t.getName());
System.out.println(t.getId());
}
}
classProcessor2extendsThread{
@Override
publicvoidrun(){
for(inti=0;i<50;i++){
System.out.println(Thread.currentThread().getName()
+"----------->"+i);
}
}
}
classProcessor3implementsRunnable{
/**
*Thread中的run方法不能丟擲異常,所以重寫runn方法之後,在run方法的宣告位置上不能使用throws
*所以run方法中的異常只能try...catch...
*/
@Override
publicvoidrun(){
for(inti=0;i<11;i++){
System.out.println(Thread.currentThread().getName()+"========>"
+i);
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
}
classProcessor4implementsRunnable{
@Override
publicvoidrun(){
try{
Thread.sleep(1000000000);
System.out.println("能否執行這裡");
}catch(InterruptedExceptione){
e.printStackTrace();
}
for(inti=0;i<11;i++){
System.out.println(Thread.currentThread().getName()+"========>"
+i);
}
}
}
classProcessor5implementsRunnable{
booleanisRun=true;
@Override
publicvoidrun(){
for(inti=0;i<11;i++){
if(isRun){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"========>"+i);
}
}
}
}
執行緒優先順序
執行緒優先順序主要分三種:MAX_PRIORITY(最高);MIN_PRIORITY(最低階)NORM_PRIORITY(標準)預設
//設定執行緒的優先順序,執行緒啟動後不能再次設定優先順序
//必須在啟動前設定優先順序
//設定最高優先順序
t1.setPriority(Thread.MAX_PRIORITY);
sleep
publicclassSleepTest{
publicstaticvoidmain(String[]args){
System.out.println("Wait");
//讓主執行緒等待5秒再執行
Wait.bySec(5);
//提示恢復執行
System.out.println("start");
}
}
classWait{
publicstaticvoidbySec(longs){
//sleeps個1秒
for(inti=0;i<s;i++){
System.out.println(i+1+"秒");
try{
//sleep1秒
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
}
停止一個執行緒
如果我們的執行緒正在睡眠,可以採用interrupt進行中斷
通常定義一個標記,來判斷標記的狀態停止執行緒的執行
yield
它與sleep()類似,只是不能由使用者指定暫停多長時間,並且yield()方法只能讓同優先順序的執行緒有執行的機會,採用yieid可以將CPU的使用權讓給同一個優先順序的執行緒
publicclassYieldTest{
publicstaticvoidmain(String[]args){
FirstThreadmt=newFirstThread();
SecThreadmnt=newSecThread();
mt.start();
mnt.start();
}
}
classFirstThreadextendsThread{
publicvoidrun(){
for(inti=0;i<5;i++){
System.out.println("第一個執行緒的第"+(i+1)+"次執行");
Thread.yield();//暫停執行緒
}
}
}
classSecThreadextendsThread{
publicvoidrun(){
for(inti=0;i<5;i++){
System.out.println("第二個執行緒的第"+(i+1)+"次執行");
Thread.yield();
}
}
}
join
當前執行緒可以呼叫另一個執行緒的join方法,呼叫後當前執行緒會被阻塞不再執行,直到被呼叫的執行緒執行完畢,當前執行緒才會執行
publicclassJoinTestextendsThread{
publicJoinTest(Stringname){
super(name);
}
publicvoidrun(){
for(inti=0;i<5;i++)
System.out.println(getName()+""+i);
}
publicstaticvoidmain(String[]args){
for(inti=0;i<10;i++){
if(i==5){
JoinTesttempjt=newJoinTest("半路加入的執行緒");
try{
tempjt.start();
tempjt.join();
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+""+i);
}
}
}
synchronized
執行緒同步,指某一個時刻,指允許一個執行緒來訪問共享資源,執行緒同步其實是對物件加鎖,如果物件中的方法都是同步方法,那麼某一時刻只能執行一個方法,採用執行緒同步解決以上的問題,我們只要保證執行緒一操作s時,執行緒2不允許操作即可,只有執行緒一使用完成s後,再讓執行緒二來使用s變數
• 非同步程式設計模型:t1執行緒執行t1的,t2執行緒執行t2的,兩個執行緒之間誰也不等誰.
• 同步程式設計模型:t1執行緒和t2執行緒執行,t2執行緒必須等t1執行緒執行結束之後,t2執行緒才能執行,這是同步程式設計模型.
•
• 什麼時候要用同步呢?為什麼要引入執行緒同步呢?
• 1.為了資料的安全,儘管應用程式的使用率降低,但是為了保證資料是安全的,必須加入執行緒同步機制.
• 執行緒同步機制使程式變成了(等同)單執行緒.
• 2.什麼條件下要使用執行緒同步?
• 第一:必須是多執行緒環境
• 第二:多執行緒環境共享同一個資料.
• 第三:共享的資料涉及到修改操作.
//synchronized是對物件加鎖
//採用synchronized同步最好只同步有執行緒安全的程式碼
//可以優先考慮使用synchronized同步塊
//因為同步的程式碼越多,執行的時間就會越長,其他執行緒等待的時間就會越長
//影響效率
publicclassTestWithdrawal{
publicstaticvoidmain(String[]args){
//建立兩個執行緒
TestAccountr=newTestAccount();
Threadone=newThread(r);
Threadtwo=newThread(r);
one.setName("張三");
two.setName("張三的妻子");
//啟動執行緒
one.start();
two.start();
}
}
classAccount{
privateintbalance=500;//餘額
publicintgetBalance(){
returnbalance;
}
//取款
publicvoidwithdraw(intamount){
balance=balance-amount;
}
}
classTestAccountimplementsRunnable{
//所有用TestAccount物件建立的執行緒共享同一個帳戶物件
privateAccountacct=newAccount();
publicvoidrun(){
for(inti=0;i<5;i++){
makeWithdrawal(100);//取款
if(acct.getBalance()<0){
System.out.println("賬戶透支了!");
}
}
}
privatevoidmakeWithdrawal(intamt){
synchronized(acct){
if(acct.getBalance()>=amt){
System.out.println(Thread.currentThread().getName()+"準備取款");
try{
Thread.sleep(500);//0.5秒後實現取款
}catch(InterruptedExceptionex){
}
//如果餘額足夠,則取款
acct.withdraw(amt);
System.out.println(Thread.currentThread().getName()+"完成取款,餘額:"+acct.getBalance());
}else{
//餘額不足給出提示
System.out.println("餘額不足以支付"
+Thread.currentThread().getName()+"的取款,餘額為"
+acct.getBalance());
}
}
}
}
死鎖
publicclassDeadLock{
publicstaticvoidmain(String[]args){
Objecto1=newObject();
Objecto2=newObject();
Threadt1=newThread(newT1(o1,o2));
Threadt2=newThread(newT2(o1,o2));
t1.start();
t2.start();
}
}
classT1implementsRunnable{
Objecto1;
Objecto2;
T1(Objecto1,Objecto2){
this.o1=o1;
this.o2=o2;
}
@Override
publicvoidrun(){
synchronized(o1){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
synchronized(o2){
}
}
}
}
classT2implementsRunnable{
Objecto1;
Objecto2;
T2(Objecto1,Objecto2){
this.o1=o1;
this.o2=o2;
}
@Override
publicvoidrun(){
synchronized(o2){
try{
Thread.sleep(1000);
}catch(InterruptedExceptione){
e.printStackTrace();
}
synchronized(o1){
}
}
}
}
守護執行緒
從執行緒分類上可以分為:使用者執行緒(以上講的都是使用者執行緒),另一個是守護執行緒。守護執行緒是這樣的,所有的使用者執行緒結束生命週期,守護執行緒才會結束生命週期,只要有一個使用者執行緒存在,那麼守護執行緒就不會結束,例如java中著名的垃圾回收器就是一個守護執行緒,只有應用程式中所有的執行緒結束,它才會結束。
• 其它所有的使用者執行緒結束,則守護執行緒退出!
• 守護執行緒一般都是無限執行的.
publicclassDaemonThread{
publicstaticvoidmain(String[]args)throwsInterruptedException{
Threadt1=newThread(newRunnable2());
t1.setName("t1");
//將t1這個使用者執行緒修改成守護執行緒.線上程沒有啟動時可以修改以下引數
t1.setDaemon(true);
t1.start();
//主執行緒
for(inti=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"----->"+i);
Thread.sleep(1000);
}
}
}
classRunnable2implementsRunnable{
@Override
publicvoidrun(){
inti=0;
while(true){
i++;
System.out.println(Thread.currentThread().getName()+"-------->"
+i);
try{
Thread.sleep(500);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
}
設定為守護執行緒後,當主執行緒結束後,守護執行緒並沒有把所有的資料輸出完就結束了,也即是說守護執行緒是為使用者執行緒服務的,當使用者執行緒全部結束,守護執行緒會自動結束
Timer.schedule()
/**
*關於定時器的應用作用:每隔一段固定的時間執行一段程式碼
*/
publicclassTimerTest{
publicstaticvoidmain(String[]args)throwsParseException{
//1.建立定時器
Timert=newTimer();
//2.指定定時任務
t.schedule(newLogTimerTask(),newSimpleDateFormat(
"yyyy-MM-ddHH:mm:ssSSS").parse("2017-06-2914:24:00000"),
10*1000);
}
}
//指定任務
classLogTimerTaskextendsTimerTask{
@Override
publicvoidrun(){
System.out.println(newSimpleDateFormat("yyyy-MM-ddHH:mm:ssSSS")
.format(newDate()));
}
}
我是melon,一個10年程式設計老司機。Q我3474203856。 給melon留言或者說明是看到文章過來的。所有相關技術問題都可以一起尋找到答案。