執行緒基礎程式碼總結
執行緒程式碼總結
1.執行緒建立第一種方式(繼承Thread類)重寫run方法
下劃線代表新的類,可自行建立
public class Demo01Thread {
public static void main(String[] args) {
MyThread m = new MyThread();
m.start();
for (int i = 0; i < 20; i++) {
System.out.println("main"+i);
}
}
}
------------------------------
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run:"+i);
}
}
}
執行緒的sleep方法
public class Demo4 {
public static void main(String[] args) {
//模擬秒錶
for (int i = 1; i <= 60; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.執行緒建立第二種方式(實現Runnable介面)重寫run方法實現Runnable介面的好處:
實現Runnable介面的好處
- 1.避免了單繼承的侷限性 實現了Runnable介面 還可以實現其他介面
- 2.增強了程式的擴充套件性 降低了耦合性
把設定執行緒任務和開啟新執行緒進行了分離 可以傳遞不同的實現類,就可以執行不同的執行緒任務
public class Demo5 {
public static void main(String[] args) {
Runnable run = new MyThread5();
Thread t = new Thread(run);
t.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
--------------------------------------
public class MyThread5 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
補充:start()和run()的區別
- run():Thread本身也是實現Runnable介面。Runnable介面只有一個方法就是run方法。啟動執行緒時,會使得run方法在那個獨立執行的執行緒中被呼叫;run方法的內容就是執行緒任務也稱為執行緒體
- start():start方法就是啟動執行緒,會導致run方法的呼叫。
- 使用start方法,是真的啟動了執行緒,達到了非同步執行的效果。而使用run方法並沒有真的啟動執行緒,而是由main執行緒去呼叫run方法,還是在main執行緒裡執行。
匿名內部類實現執行緒的建立(函數語言程式設計也可以)
public class Demo6 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
});
t.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
3.由多執行緒引出的執行緒安全問題:多執行緒訪問共享資料,就會產生經典的賣票問題(出現0、-1或者同時出現100)
解決:解決:保證同一時間只有一個執行緒在操作共享資料。即執行緒同步技術
3.1同步程式碼塊(推薦使用)
public class RunnableImpl implements Runnable{
//定義一個多個執行緒共享的票源
private int ticket = 100;
//建立一個鎖物件 保證其唯一性
Object o = new Object();
@Override
public void run() {
while (true){
//建立一個同步程式碼塊
synchronized (o){
if(ticket>0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在賣第"+ticket+"張票");
ticket--;
}
}
}
}
}
--------------------------------
public class Temo4 {
public static void main(String[] args) {
Runnable run = new RunnableImpl();
Thread t = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t.start();
t1.start();
t2.start();
}
}
過程解釋:
t搶到了CPU執行權,執行run方法,遇到synchronized程式碼塊,此時t檢查synchronized程式碼塊是否有鎖物件,發現有,就會獲取鎖物件,進入同步執行;t2搶到了CPU執行權,執行run方法,遇到synchronized程式碼塊,此時t2檢查synchronized程式碼塊是否有鎖物件,發現沒有,t2進入阻塞狀態,會一直等待t歸還鎖物件,一直到t執行完同步程式碼塊,把鎖物件歸還給同步程式碼塊,此時t2獲得鎖物件,進入同步程式碼塊執行。
同步中的執行緒,沒有執行完畢不會釋放鎖,同步外的執行緒沒有鎖進不去同步
3.2同步方法
public class RunnableImpl implements Runnable{
//定義一個多個執行緒共享的票源
private int ticket = 100;
//建立一個鎖物件 保證其唯一性
Object o = new Object();
@Override
public void run() {
payTicket();
}
/*
定義一個同步方法
*/
public synchronized void payTicket() {
while (true){
//建立一個同步程式碼塊
synchronized (o){
if(ticket>0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在賣第"+ticket+"張票");
ticket--;
}
}
}
}
}
----------------------------------
public class Temo5 {
public static void main(String[] args) {
Runnable run = new RunnableImpl();
Thread t = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t.start();
t1.start();
t2.start();
}
}
3.Lock鎖
public class RunnableImpl implements Runnable{
//1.在成員位置建立一個ReentrantLock物件
Lock l = new ReentrantLock();
private int ticket = 100;
@Override
public void run() {
while (true){
//2.在可能出現安全問題的程式碼前呼叫lock方法獲取lock鎖
l.lock();
if(ticket>0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName()+"-->正在賣第"+ticket+"張票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//3.在可能出現安全問題的程式碼後釋放lock方法獲取lock鎖
//無論程式是否異常 都釋放鎖
l.unlock();
}
}
}
}
}
---------------------------------
public class Demo1 {
public static void main(String[] args) {
Runnable run = new RunnableImpl();
Thread t = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t.start();
t1.start();
t2.start();
}
}
4.執行緒通訊
概念:多個執行緒在處理同一資源,但是各自執行緒的任務卻不相同,所以存線上程通訊
wait()和notify()必須由同一個鎖物件呼叫(重點)
notify()和wait()必須在同步程式碼塊或同步函式中使用(重點)
4.1示例 包子問題
Step1:建立一個資源類
public class BaoZi {
String pi;
String xian;
//flag表示是否有包子
boolean flag = false;
}
Step2:建立包子鋪執行緒類
/*
使用包子物件作為鎖物件
*/
public class BaoZiPu extends Thread{
private BaoZi bz;
public BaoZiPu(BaoZi bz) {
this.bz = bz;
}
//執行緒任務 生產包子
@Override
public void run() {
int count = 0;
while (true) {
synchronized (bz) {
if (bz.flag==true) {
try {
//有包子,包子鋪執行緒等待
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被喚醒之後執行,包子鋪生產包子 生產兩種包子 根據count% 2==0的結果
if(count%2==0) {
bz.pi = "薄皮";
bz.xian = "三鮮";
}else {
bz.pi = "冰皮";
bz.xian = "牛肉";
}
count++;
System.out.println("包子鋪正在生產"+bz.pi+bz.xian+"包子");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bz.flag=true;
//喚醒吃貨執行緒
bz.notify();
System.out.println("包子鋪已經生產"+bz.pi+bz.xian+"包子,可以開始吃了");
}}
}
}
Step3:建立吃貨執行緒類
public class ChiHuo extends Thread{
private BaoZi bz;
public ChiHuo(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
while (true) {
synchronized (bz) {
if(bz.flag==false){
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被喚醒之後執行的程式碼
System.out.println("吃貨正在吃:"+bz.pi+bz.xian+"的包子");
bz.flag=false;
bz.notify();
System.out.println("吃貨已經吃完了:"+bz.pi+bz.xian+"的包子"+"...包子鋪開始生產包子");
}
}
}
}
Step4:測試類
public class Demo {
public static void main(String[] args) {
BaoZi bz = new BaoZi();
//開啟包子鋪執行緒 生產包子
new BaoZiPu(bz).start();
//開啟吃貨執行緒,吃包子
new ChiHuo(bz).start();
}
}
執行結果截圖:
5.執行緒池
Executor 執行緒池的工廠類 用來生成執行緒池
submit(Runnable task)提交一個Runnable任務用於執行
void shutdown() 關閉銷燬執行緒池的方法
使用步驟
1:使用靜態方法newFixedThreadPool生成一個指定執行緒數量的執行緒池
2:建立一個類,實現Runnable介面,重寫run方法,設定執行緒任務
3:呼叫ExecutorService的方法submit,傳遞執行緒任務,開啟執行緒,執行run方法
4:呼叫ExecutorService的方法shutdown,銷燬執行緒池(不建議執行)
public class Demo1 {
public static void main(String[] args) {
//1.
ExecutorService es = Executors.newFixedThreadPool(2);
//3.
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.shutdown();
}
}
-----------
//2.
public class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"建立了一個新的執行緒執行");
}
}