進程:
進程是用于指一個正在運行的可執行程序,它可以包含多個線程,注意:一個沒有運行的程序不是一個進程。
進程的特點:每一個進程都有自己的獨立的一塊內存空間、一組資源系統。其內部數據和狀態都是完全獨立的。
進程的優點是提高CPU運行效率,在同一時間內執行多個程序,即并發執行。但是從嚴格上講,也不是絕對的同一時刻執行多個程序,只不過CPU在執行時通過時間片等調度算法不同進程高速切換,由于速度太快,所以我感覺不到在切換 而已。
線程:
線程在一個進程 中負責了代碼的執行,就是進程中執行路徑,
可以理解為進程的多條執行線索,每條線索又對應著各自獨立的生命周期。線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。一個線程可以創建和撤銷另一個線程,同一個進程中的多個線程之間可以并發執行。
運行任何一個java程序,jvm在運行的時候都會創建一個main線程執行main方法中所有代碼。
一個java應用程序至少有兩個線程, 一個是主線程負責main方法代碼的執行,一個是垃圾回收器線程,負責了回收垃圾。
Java中的線程要經歷4個過程
1.創建
創建一個Java線程常見的有兩種方式:
繼承Thread類和實現Runnable接口這兩種方式。
2.執行
線程創建后僅僅占有了內存資源,在JVM管理的線程中還沒有該線程,該線程必須調用start方法通知JVM,這樣JVM就會知道又有一個新的線程排隊等候了。如果當前線程輪到了CPU的使用權限的話,當前線程就會繼續執行。
3.中斷
a.JVM將CPU的使用權限從當前線程切換到其它線程,使本線程讓出CPU的使用權限而處于中斷狀態。
b.線程在執行過程中調用了sleep方法,使當前線程處于休眠狀態。
c.線程在執行的過程中調用wait方法
d.線程在使用cpu資源期間,執行了某個操作而進如阻塞狀態。
4.死亡
死亡的線程不在具有執行能力。線程死亡的原因有二:
a.線程正常運行結束而引起的死亡,即run方法執行完畢。
b.線程被提前強制終止。
多線程的好處:
1. 解決了一個進程能同時執行多個任務的問題。
2. 提高了資源的利用率。
多線程 的弊端:
1. 增加cpu的負擔。
2. 降低了一個進程中線程的執行概率。
3. 引發了線程安全 問題。
4. 出現了死鎖現象。
線程的創建方式:
第一種創建方式:創建Thread的子類
創建Thread類的子類,并重寫run()方法,該方法代表了該線程完成的任務。run方法為線程執行體。
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
}
}
// 可以用如下方式創建并運行上述Thread子類
MyThread myThread = new MyThread();
myTread.start();```
一旦線程啟動后start方法就會立即返回,而不會等待到run方法執行完畢才返回。就好像run方法是在另外一個cpu上執行一樣。當run方法執行后,將會打印出字符串MyThread running。
也可以如下創建一個Thread的匿名子類:
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
}
};
thread.start();當新的線程的run方法執行以后,計算機將會打印出字符串
"Thread Running"```。
第二種創建方式:實現Runnable接口
第二種編寫線程執行代碼的方式是新建一個實現了java.lang.Runnable接口的類的實例,實例中的方法可以被線程調用。
public class MyRunnable implements Runnable {
public void run(){
System.out.println("MyRunnable running");
}
}```
為了使線程能夠執行run()方法,需要在Thread類的構造函數中傳入 MyRunnable的實例對象。示例如下:
Thread thread = new Thread(new MyRunnable());
thread.start();```
當線程運行時,它將會調用實現了Runnable接口的run方法。上例中將會打印出"MyRunnable running"
。
同樣,也可以創建一個實現了Runnable接口的匿名類,如下所示:
Runnable myRunnable = new Runnable(){
public void run(){
System.out.println("Runnable running");
}
}
Thread thread = new Thread(myRunnable);
thread.start();
Java線程間通訊的小例子:模擬生產者和消費者,生產者生產一個,消費者購買一個,生產者未生產完成消費者就不能購買,需要使用到Object類的wait()和notify()方法,及鎖對象
package com.sey.thread1;
/*
* 需求:模擬生產者和消費者,生產者生產蘋果和香蕉,消費者購買,并且消費者購買一個生產者才會生產一個,不浪費產品
*
*/
// 產品類
class Product {
String name; // 產品的名稱
double money; // 產品的價格
boolean flag = false; // 是否生產完成的標識,當生產完成時為true,銷售完成后為false
}
// 生產者
class Producer extends Thread {
Product p; // 產品
public Producer(Product p) {
this.p = p;
}
@Override
public void run() {
int i = 0;
while(true) {
synchronized(p) {
// 當產品銷售完成的時候,生產產品
if (p.flag == false) {
if (i % 2 == 0) {
// 生產蘋果
p.name = "蘋果";
p.money = 6.0;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
// 生產香蕉
p.name = "香蕉";
p.money = 3.0;
}
System.out.println("生產者生產了 " + p.name + "價格 " + p.money);
p.flag = true; // 記錄生產完成
p.notify(); // 通知消費者我已經生產完成,消費者好趕緊購買,其實是喚醒以```鎖對象為標識符```的線程池中其中一個線程
i++;
} else {
try {
p.wait(); // 等待消費者購買
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
// 消費者
class Consumer extends Thread {
Product p; // 產品
public Consumer(Product p) {
this.p = p;
}
@Override
public void run() {
// 當生產者生產完成時消費者就可以購買了
while(true) {
synchronized(p){
if (p.flag == true) {
System.out.println("消費者購買了" + p.name + "價格 " + p.money);
p.flag = false;
p.notify(); // 通知生產者我已經購買,生產者好進入生產狀態
} else {
try {
p.wait(); // 產品還未生產完成,等待生產者生產完成
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
// 創建產品對象
Product p = new Product();
// 創建生產者對象
Producer producer = new Producer(p);
producer.start();
// 消費者對象
Consumer consumer = new Consumer(p);
consumer.start();
}
使用代碼
關于多線程的題
-1.同步代碼塊synchronized() {}
要4個要注意事項:
a. 任意的一個對象都可以做為鎖對象。
b. 在同步代碼塊中調用了sleep方法并不是釋放鎖對象的。
c. 只有真正存在線程安全問題的時候才使用同步代碼塊,否則會降低效率的。
d. 多線程操作的鎖 對象必須 是唯一共享 的。否則無效。
0.出現線程安全問題的兩個根本原因:
a. 存在兩個或者兩個以上 的線程對象,而且線程之間共享著一個資源。
b. 有多個語句操作了共享資源。
1.Java中多線程同步是什么?
答:java中的線程程同步,是為了實現對共享資源的訪問,如果沒有過線程程同步機制,則會導致,當
某個線程在修改某個共享變量時,另一個線程程同時正在使用或更新同一個共享變量就會導致程序出錯
2.線程有幾種有哪些實現方法?
答:有兩種:第一種通過繼承Thread類,但這種方式只能單繼承,第二種方法,通過繼承Runnable類,這樣可以
實現多繼承。
3.Thread.start()和Thread.run()有什么區別?
答:Thread.start()方法是啟動線程的,通過調用它,使線程進入就緒狀態,當處于就緒狀態的進程獲得CPU后,
JVM會自動調用Thread.run()方法,使進程進入運行狀態。
4.為什么需要run()和start()方法,我們可以只用run()方法來完成任務嗎?
答:因為j由java創建一個單獨的線程不同于其他方法的調用,這項工作由java的start()方法來
實現,另一個好處就是任何一個對象都可以作為進程運行,只要繼承了runnable()方法,這就避免了因為
繼承了Thread類而造成的多繼承問題。
5.sleep(),suspend()和wait()有什么區別?
答:sleep(mills)方法使進程處于睡眠mills(s),在這段時間內進程處于非運行狀態,線程一直持有
對象的監視器。sleep()方法是一個靜態方法,只對當前進程有效;suspend()方法,也是使
進程進入阻塞狀態,其他進程調用resume方法可將其喚醒,使用suspend()方法,容易造成死鎖問題
wait(mills)使進程進入阻塞狀態,在mills(s)內可由其他進程調用notity()方法將其喚醒,sleep()方法
和wait()方法的區別在于,sleep()方法是進程調用的,而wait()方法是對象調用的。
6.當一個同步方法已經執行,線程能調用對象上的非同步實例嗎?
答:可以的,一個非同步方法總是可以被調用的。同步方法:synchronized修飾,把訪問臨界區的方法
定義為同步方法,java中沒有對非同步方法做任何檢查,鎖對象僅僅會檢查同步方法。
7.在一個對象上兩個進程可以調用兩個不同的同步實例方法嗎?
答:不能,因為,當一個進程同步實例后,便獲得了該進程的對象鎖,只有在該進程的對象鎖釋放后
才能執行其他同步方法。
8.什么事死鎖?
答:死鎖就是兩個或兩個以上的進程無限的阻塞,一直相互等待所需資源。
9.進程和線程有什么區別?
答:進程是一個具有獨立運行功能的程序關于某個數據集合的一次活動,是動態的,一個進程包含了多個線程,
進程是資源分配的最小單位,線程是獨立運行和獨立調度的基本單位,不同的進程擁有不同的內存空間,多個線程
利用相同的數據空間,每個線程都有單的的棧內存來存儲本地數據。
10.什么事線程安全,Vector是一個線程安全類嗎?
答:如果一個進程中有多個線程,多個線程同時運行這段代碼,如果每次運行的結果和單線程運行的結果是一樣的,
而且其他變量的值也都是和預期 的是一樣的,那么就是線程安全的。Vector是線程安全的類。
11.java中如何停止一個線程?
答:java中并沒用提供停止線程的API,像stop(),wait()等操作容易造成死鎖問題,解決的方法就是在run()方法里設計
循環,利用boolean變量作為條件跳出循環結束線程,或者取消任務,不然只能等待進程執行結束。
12.java中notify()和notifyAll()有什么區別?notifyAll()
答:notify()不能喚醒某個指定等待的線程,所以只能用于只有一個等待進程的情況下,而notifyAll()喚醒所有
進程并允許他們搶鎖確保了至少有一個進程能繼續運行。
13.為什么notify和wait方法要在同步塊中調用?
答:主要是因為Java API強制要求這樣做,如果你不這么做,你的代碼會拋出IllegalMonitorStateException異常。
還有一個原因是為了避免wait和notify之間產生競態條件。
14.什么是線程池?為什么要調用它?
答:線程的創建時花費大量的資源和時間,如果等任務來了再創建的話響應的時間會比較長,而且一個進程能夠創建的線程
數量是有限的,為了解決這個問題,在程序一開始執行的時候,便創建若干個線程響應處理,被稱為線程池。利用Excutor框架可以,
不同的線程池。
15.如何寫代碼實現生產者消費者問題?
答:后面代碼有答案
16.如何避免死鎖?
答:進程造成死鎖的必要條件為:互斥條件,請求和保持條件,不剝奪條件,循環等待條件,因此要避免死鎖,就要破壞這些條件
中的一個,然而互斥條件是進程必須的,所以的破壞后三個條件,最簡單的方法就是破壞循環等待條件。
17.線程中的yield方法有什么用?
答:當線程執行yield方法后,便放棄了對CPU的占用權。
18.java怎樣喚醒一個阻塞的進程?
答:wait()方法導致阻塞,利用Notify()或notifyAll()方法來喚醒;
sleep方法阻塞,在睡眠mills(s)重新進入就緒狀態;
susPend方法阻塞,利用resume()方法喚醒。