Java多線程(一):
https://blog.csdn.net/Veer_c/article/details/103842078
Java多線程(二):
https://blog.csdn.net/Veer_c/article/details/103842263
Java多線程(三):
https://blog.csdn.net/Veer_c/article/details/103842317
Java多線程(四):
https://blog.csdn.net/Veer_c/article/details/103842602
線程中的一些方法
1.線程加入
public final void join()
等待該線程中止,其他線程才能繼續搶着執行,因爲線程執行的時候,每個線程都會搶佔CPU的執行權,所以我們可以利用此方法先讓一個線程執行完畢後,然後再去執行其他的線程。
package com.edu_01;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"--"+i);
}
}
}
public class Test {
public static void main(String[] args) {
//創建三個線程
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
//給線程起名字
mt1.setName("劉備");
mt2.setName("曹操");
mt3.setName("孫權");
//開啓三個線程
mt1.start();
//接着讓mt1這個線程設置爲加入線程,其他線程就沒有搶佔cpu執行權的權利了,只能等待該線程執行完畢之後,才能開始搶佔
try {
mt1.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mt2.start();
mt3.start();
}
}
2.線程禮讓
public static void yield():暫停當前正在執行的線程對象,並執行其他線程。
作用:讓線程間的執行更和諧一些,也就是讓線程搶佔CPU的概率相同一點。
package com.edu_02;
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.getName()+"---"+i);
//實現線程禮讓
Thread.yield();
}
}
}
public class Test {
public static void main(String[] args) {
//創建兩個線程
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
//給線程設置名字
mt1.setName("郭德綱");
mt2.setName("周立波");
//開啓線程
mt1.start();
mt2.start();
}
}
3.線程死亡
public final void stop():直接殺死(即終止當前運行的線程,線程不會往下執行)
public void interrupt():直接殺死,在死前,還可以有遺言。(也就是說,線程還可以將該程序執行完)
package com.edu_03;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MyThread extends Thread{
@Override
public void run() {
//打印一下開始執行的時間
System.out.println("開始時間:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
//休眠10秒鐘
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("我被殺死了");
}
System.out.println("結束時間:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
public class Test {
public static void main(String[] args) {
//創建線程對象
MyThread mt = new MyThread();
//開啓線程對象
mt.start();
//在線程處於睡眠的過程中將他殺死
try {
Thread.sleep(3000);
//殺死剛剛開啓的線程
//調用stop()方法將線程直接殺死
//mt.stop();//劃了一條橫線表示該方法已經過時,但是還可以使用
//interrupt():直接殺死,在死前,還可以有遺言。
mt.interrupt();//線程被殺死之後會將後面的代碼執行完畢之後,再死去
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4.線程休眠
static void sleep(long millis) 線程睡一會,讓線程休眠,一定會醒來的。
線程的生命週期
1.新建:線程的對象調用Start()方法
2.就緒:線程有cpu的執行權,但是沒有CPU執行資格,開始搶佔CPU的執行權
3.運行:搶佔到了CPU的執行權,開始執行。(如果被其他線程搶到了執行權,則會恢復到就緒狀態)
4.有可能阻塞:線程對象調用sleep方法,wait方法,在線程睡醒之後,或者被喚醒之後,就會恢復到就緒狀態,
5.死亡:線程調用stop方法,或者調用interrupt方法,或者在線程執行完run方法後。
再來張顏色不單調的。
下面對線程生命週期中的 7 種狀態做說明:
出生狀態:用戶在創建線程時所處的狀態,在用戶使用該線程實例調用 start() 方法之前,線程都處於出生狀態。
就緒狀態:也稱可執行狀態,當用戶調用 start() 方法之後,線程處於就緒狀態。
運行狀態:當線程得到系統資源後進入運行狀態。
等待狀態:當處於運行狀態下的線程調用 Thread 類的 wait() 方法時,該線程就會進入等待狀態。進入等待狀態的線程必須調用 Thread 類的 notify() 方法才能被喚醒。notifyAll() 方法是將所有處於等待狀態下的線程喚醒。
休眠狀態:當線程調用 Thread 類中的 sleep() 方法時,則會進入休眠狀態。
阻塞狀態:如果一個線程在運行狀態下發出輸入/輸出請求,該線程將進入阻塞狀態,在其等待輸入/輸出結束時,線程進入就緒狀態。對阻塞的線程來說,即使系統資源關閉,線程依然不能回到運行狀態。
死亡狀態:當線程的 run() 方法執行完畢,線程進入死亡狀態。
提示:一旦線程進入可執行狀態,它會在就緒狀態與運行狀態下輾轉,同時也可能進入等待狀態、休眠狀態、阻塞狀態或死亡狀態。
使線程處於就緒狀態有如下幾種方法。
調用 sleep() 方法。
調用 wait() 方法。
等待輸入和輸出完成。
當線程處於就緒狀態後,可以用如下幾種方法使線程再次進入運行狀態。
線程調用 notify() 方法。
線程調用 notifyAll() 方法。
線程調用 intermpt() 方法。
線程的休眠時間結束。
輸入或者輸出結束。
線程間通信(生產消費者問題):不同類型線程針對同一個資源的操作
1.系統不僅要賣票還要入票,每次生產一張票,就會賣出去一張票,稱爲單生產單消費問題。
2.不僅要賣肉夾饃還要生產肉夾饃,可以利用多個線程去生產肉夾饃,然後利用多個線程去出售,稱爲多生產多消費問題。
案例:以給學生設置和獲取姓名和年齡爲例,演示線程通信問題
線程間通訊:
資源:Student
設置數據線程:SetThread
獲取數據線程:GetThread
測試類:StudentDemo
package com.edu_04;
public class Student {
String name;
int age;
}
public class SetThread implements Runnable{
private Student s;
private int x=0;
public SetThread(Student s){
this.s = s;
}
@Override
public void run() {
//給學生對象設置姓名和年齡
//Student s = new Student();
while (true) {
synchronized (s) {
if (x%2==0) {
s.name = "劉嘉玲";
s.age = 50;
}else {
s.name = "陳冠希";
s.age = 35;
}
x++;
}
}
}
}
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}
@Override
public void run() {
//獲取線程,獲取學生對象的姓名和年齡
//Student s = new Student();
while (true) {
synchronized (s) {
System.out.println(s.name+"--"+s.age);
}
}
}
}
public class StudentDemo {
public static void main(String[] args) {
//創建一個學生對象
Student s = new Student();
//創建設置和獲取線程,並開啓線程
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//開啓線程
t1.start();
t2.start();
}
}
問題1:控制檯出現的結果是:null—0
因爲我們在在創建對象的時候,setThread()和getThread()的對象不是同一個,所以在輸出的時候,因爲setThread()還沒有設置對象,所以我在傳遞參數的時候可以自己創建構造方法,讓這個參數一致
設置和獲取線程使用的學生資源不是同一個,把資源作爲構造參數傳遞即可。
問題2:
相同的數據出現了多次,CPU的一點點時間就足夠我們的程序執行很多次
數據出現了問題(數據安全問題)
a:是否是多線程環境 是
b:是否有共享數據 是
c:是否有多條語句操作共享數據 是
既然我們知道它是出現了數據安全問題,我們就應該來解決它。
如何解決呢?加鎖
問題3:加了鎖以後,數據還是有問題
A:多個線程都要加鎖
B:多個線程加的鎖必須是同一把鎖
因爲,線程在執行的時候,每個線程的都會有搶佔的隨機性,所以輸出的時候沒有那麼和諧,而且會出現錯誤的數據,出現線程不安全問題,所以我們要給線程加鎖,保證線程數據安全,我們可以用等待,喚醒機制保證數據的和諧
將上述代碼使用等待喚醒機制改進,實現禮讓效果
package com.edu_05;
public class Student {
String name;
int age;
boolean flag;//在這裏可以作爲對象的一個標記,如果是false說明該對象沒有數據,如果是true說明該對象有數據
}
public class SetThread implements Runnable{
private Student s;
private int x = 0;
public SetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
//判斷該對象此時有沒有數據
if (s.flag) {
//等待
try {
s.wait();//設置線程等待,釋放鎖s
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (x%2==0) {
s.name = "劉嘉玲";
s.age = 50;
}else {
s.name = "冠希";
s.age = 35;
}
x++;//x=1
//此時對象有數據了
s.flag = true;
s.notify();//如果有等待的線程就喚醒,如果沒有等待的線程,則沒有任何效果
}//在此時釋放鎖對象s
}
}
}
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
//判斷對象有沒有數據
if (!s.flag) {
//等待設置線程給對象設置數據
try {
s.wait();//獲取線程處於等待狀態,釋放鎖對象s,在哪裏跌倒在哪裏爬起來
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(s.name+"--"+s.age);//劉嘉玲--50
//冠希--35
//劉嘉玲--50
//當獲取線程從學生對象中獲取了數據之後,我們就默認他已經沒有數據了,此時我們應該
//繼續讓設置線程繼續給學生對象設置信息
s.flag = false;
s.notify();
}
}
}
}
public class StudentDemo {
public static void main(String[] args) {
//創建學生對象
Student s = new Student();
//創建設置線程和獲取線程
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//開啓線程
t1.start();
t2.start();
}
}
將上述代碼繼續優化:可以將上述代碼封裝成方法
1.私有化Student類的成員變量
2.在類的內部提供設置和獲取的同步方法
public class Student {
//塊編輯(alt+shift+a):在使用塊編輯的時候,一定要將輸入法切換到英文輸入法,不然會出問題
private String name;
private int age;
private boolean flag;//在這裏可以作爲對象的一個標記,如果是false說明該對象沒有數據,如果是true說明該對象有數據
//提供公共的方法設置信息
public synchronized void setInfo(String name,int age){
if (this.flag) {
//等待
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//沒有值的話,在這裏給對對象設置數據
this.name = name;
this.age = age;
//更改標記,喚醒獲取線程獲取數據
this.flag = true;
this.notify();
}
//提供公共的方法獲取信息
public synchronized void getInfo(){
if (!this.flag) {
//沒有值
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//有數據,取數據
System.out.println(this.name+"--"+this.age);
//取完數據之後,就沒有數據了
this.flag = false;
this.notify();
}
}
public class SetThread implements Runnable{
private Student s;
private int x = 0;
public SetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
if (x%2==0) {
s.setInfo("劉嘉玲", 50);
}else {
s.setInfo("陳冠希", 35);
}
x++;//x=1
}
}
}
public class GetThread implements Runnable{
private Student s;
public GetThread(Student s){
this.s = s;
}
@Override
public void run() {
while (true) {
s.getInfo();
}
}
}
public class StudentDemo {
public static void main(String[] args) {
//創建學生對象
Student s = new Student();
//創建設置線程和獲取線程
SetThread st = new SetThread(s);
GetThread gt = new GetThread(s);
Thread t1 = new Thread(st);
Thread t2 = new Thread(gt);
//開啓線程
t1.start();
t2.start();
}
}
Java多線程(一):
https://blog.csdn.net/Veer_c/article/details/103842078
Java多線程(二):
https://blog.csdn.net/Veer_c/article/details/103842263
Java多線程(三):
https://blog.csdn.net/Veer_c/article/details/103842317
Java多線程(四):
https://blog.csdn.net/Veer_c/article/details/103842602