文章目錄
- 概念
- 線程建立
-
- 繼承Thread類
- 實作Runnable接口
- 實作Callable接口
- 補充知識點
-
- 靜态代理模式
- Lamda表達式
- 線程狀态
-
- 線程停止(stop)
- 線程休眠(sleep)
- 線程禮讓(yield)
- 線程強制執行(join)
- 線程優先級
- 守護程序
- 線程同步
-
- 線程不安全舉例
-
- 不安全買票
- 不安全銀行
- 不安全集合
- 同步方法
-
- 同步方法,同步塊(synchronized)
-
- 安全買票代碼
- 安全取錢代碼
- 安全list代碼
- JUC
- 死鎖
- Lock鎖
-
- 不加鎖的情況
- 加鎖的代碼
- 線程協作
-
- 生産者消費者問題
- 管程法
- 信号燈法
- 線程池
-
- 特點
- 學習連結
概念
注意:很多多線程是模拟出來的,真正的多線程是指有多個cpu,即多核,如服務 器。如果是模拟出來的多線程,即在一個cpu的情況下,在同一個時間點,cpu隻能 執行一個代碼,因為切換的很快,是以就有同時執行的錯覺。
- 線程就是獨立的執行路徑;
- 在程式運作時,即使沒有自己建立線程,背景也會有多個線程,如主線程,gc線程;
- main() 稱之為主線程,為系統的入口,用于執行整個程式;
- 在一個程序中,如果開辟了多個線程,線程的運作由排程器安排排程,排程器是與作業系統緊密相關的,先後順序是不能認為的幹預的。
- 對同一份資源操作時,會存在資源搶奪的問題,需要加入并發控制;
- 線程會帶來額外的開銷,如cpu排程時間,并發控制開銷。
- 每個線程在自己的工作記憶體互動,記憶體控制不當會造成資料不一緻
線程建立
繼承Thread類
1.繼承Thread類
2.重寫run方法
3.建立對象調用star();
測試
package collectionstest;
import java.lang.*;
public class ThreadTest {
public static void main(String[] args) {
MyThread mt1 = new MyThread("1");
MyThread mt2 = new MyThread("2222222222222222222222222222222222222222222222");
MyThread mt3 = new MyThread("33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333");
mt1.start();
mt2.start();
mt3.start();
for(int i = 0;i<100;i++) {
System.out.println("666666666666666666666");
}
}
}
class MyThread extends Thread{
private String k;
MyThread(String n){
this.k = n;
}
@Override
public void run() {
for(int i = 0;i<100;i++) {
System.out.println(k);
}
}
}
結果
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPR10MZpWT6VFROBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4IDOxMTMxkDMzETNwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
實作Runnable接口
1.實作Runnable接口
2.實作run方法
3.建立對象,調用start方法
測試:
package collectionstest;
public class RunnableTest {
public static void main(String[] args) {
MyRunnable mr =new MyRunnable("0");
Thread myThread = new Thread(mr);
myThread.start();
MyRunnable mr1 =new MyRunnable("11111111");
Thread myThread1 = new Thread(mr1);
myThread1.start();
}
}
class MyRunnable implements Runnable{
private String k;
MyRunnable(String n){
this.k = n;
}
@Override
public void run() {
for(int i = 0;i<100;i++) {
System.out.println(k);
}
}
}
結果
實作Callable接口
測試代碼
package collectionstest;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class myCallable implements Callable<Boolean>{
private String msg;
myCallable(String s){
msg = s;
}
public Boolean call() {
System.out.println(msg);
return true;
}
}
public class CallableTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//建立目标對象
myCallable mc0 = new myCallable("1");
myCallable mc1 = new myCallable("22");
myCallable mc2 = new myCallable("333");
//建立執行服務
ExecutorService es = Executors.newFixedThreadPool(3);
//送出執行
Future<Boolean> f0 = es.submit(mc0);
Future<Boolean> f1 = es.submit(mc1);
Future<Boolean> f2 = es.submit(mc2);
//擷取結果
boolean r0 = f0.get();
boolean r1 = f1.get();
boolean r2 = f2.get();
System.out.println(""+r0 + " " + r1 + " "+ r2);
//關閉服務
es.shutdownNow();
}
}
結果
補充知識點
靜态代理模式
也就是讓一個類幫助另一個類做事情(可以做一些額外的準備)。
代碼
interface Marry{
void happyMarry();
}
class Human implements Marry{
String name;
Human(String s){
name = s;
}
public void happyMarry() {
System.out.println(name + "結婚了");
}
}
class WeddingCompany implements Marry {
private Human human;
WeddingCompany(Human a){
human = a;
}
private void before() {
System.out.println("結婚前的準備");
}
private void after() {
System.out.println("結婚後的整理");
}
public void happyMarry() {
before();
human.happyMarry();
after();
}
}
public class staticPorxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new Human("一個人"));
weddingCompany.happyMarry();
}
}
結果
Lamda表達式
隻用一次的類: 内部類 -> 匿名内部類 -> 局部内部類 -> 匿名内部類 -> lamda表達式。
特點:
避免匿名内部類定義過多
代碼很簡潔,隻留下核心的邏輯。
函數式接口:
隻有唯一一個抽象方法。可以通過lamda表達式來建立該接口的對象。
代碼
package ProcessThreadTest;
interface MyInterface{
public void function();
}
interface MyInterface1{
public void function(String a);
}
class ClassOne implements MyInterface{
public void function() {
System.out.println("普通類實作接口");
}
}
public class lamdaTest {
//靜态内部類
static class ClassTwo implements MyInterface{
public void function() {
System.out.println("靜态内部類實作接口");
}
}
public static void main(String[] args) {
//普通方法調用不使用lamda表達式
MyInterface myclass = new ClassOne();
myclass.function();
//靜态方法調用
myclass = new ClassTwo();
myclass .function();
//局部内部類調用
class ClassThree implements MyInterface{
public void function() {
System.out.println("局部内部類實作接口");
}
}
myclass = new ClassThree();
myclass.function();
//匿名内部類
myclass = new MyInterface() {
public void function() {
System.out.println("匿名内部類實作接口");
}
};
myclass.function();
//lamda表達式實作
myclass = ()->{
System.out.println("lamda表達式");
};
myclass.function();
//帶參數
MyInterface1 myclass1 = (String a)->{
System.out.println("參數是:"+a);
};
myclass1.function("帶參數");
//省略參數類型
myclass1= myclass1 = (a)->{
System.out.println("參數是:"+a);
};
myclass1.function("省略類型");
//省略括号
myclass1= myclass1 = a->{
System.out.println("參數是:"+a);
};
myclass1.function("省略括号");
//省略花括号(隻有一行)
myclass1= myclass1 = a -> System.out.println("參數是:"+a);
myclass1.function("省略花括号");
}
}
結果
線程狀态
線程的幾種狀态:
Thread類的一些重要的方法:
線程停止(stop)
不建議使用stop方法和destory方法。
從Thread類的源碼中可以看到,這個stop方法已經廢棄。
建議線程通過循環次數自動停止下來(上文已實作)或者設定标志位。
标志位代碼
package ProcessThreadTest;
class MyTheadStop implements Runnable{//此處不能繼承Thread類,因為stop方法在Thread類中是
boolean flag = true;
public void run() {
while(flag ==true) {
System.out.println("hi!");
}
}
public void stop() {
this.flag = false;
}
}
public class ThreadStopTest {
public static void main(String[] args) {
MyTheadStop mts = new MyTheadStop();
Thread myThread = new Thread(mts);
myThread.start();
for(int i = 0;i<300;i++) {
if(i == 150) {
mts.stop();
System.out.println("線程停止了");
}
System.out.println(i);
}
}
}
. . . . . . . . .
線程休眠(sleep)
sleep (時間)指定目前線程阻塞的毫秒數;
sleep存在異常InterruptedException;
sleep時間達到後線程進入就緒狀态;
sleep可以模拟網絡延時,倒計時等。
每一個對象都有一個鎖,sleep不會釋放鎖;
代碼
package ProcessThreadTest;
import java.sql.Time;
import java.util.Date;
public class SleepTest implements Runnable {
private int ticketNums = 1;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
Date date = new Date() ;
if(ticketNums > 10) {
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + date + ":拿到了第"+ ticketNums +"張票");
ticketNums++;
}
}
public static void main(String[] args) {
SleepTest sleepTest = new SleepTest();
new Thread(sleepTest,"小明").start();
new Thread(sleepTest,"小紅").start();
new Thread(sleepTest,"小亮").start();
}
}
結果(讀資料之前沒有加鎖,暫不解決)
線程禮讓(yield)
禮讓線程,讓目前正在執行的線程暫停,但不阻塞。
将線程從運作狀态轉為就緒狀态,讓cpu重新排程,禮讓不一定成功!
測試代碼
package ProcessThreadTest;
class myYield implements Runnable{
private String name;
myYield(String a){
name = a;
}
public void run() {
System.out.println(name + "禮讓前");
Thread.yield();
System.out.println(name + "禮讓後");
}
}
public class YieldTest {
public static void main(String[] args) {
new Thread( new myYield("小明") ).start();
new Thread( new myYield("小紅") ).start();
}
}
運作結果
線程強制執行(join)
Join合并線程,待此線程執行完成後,再執行其他線程,其他線程阻塞 ,可以想象成插隊
測試代碼
package ProcessThreadTest;
public class JoinTest implements Runnable {
public static void main(String[] args) throws InterruptedException {
JoinTest jt = new JoinTest();
Thread thread = new Thread(jt);
thread.start();
for(int i = 0;i<100;i++) {
System.out.println("main:"+i);
if(i==50) {
thread.join();
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<100;i++) {
System.out.println(" join:"+i);
}
}
}
運作結果
線程優先級
Java提供一個線程排程器來監控程式中啟動後進入就緒狀态的所有線程,線程排程 器按照優先級決定應該排程哪個線程來執行。線程的優先級用數字表示,範圍從1~10。
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
getPriority()擷取優先級,setPriority(int xxx)設定優先級。
守護程序
特點
- 線程分為使用者線程和守護線程
- 虛拟機必須確定使用者線程執行完畢
- 虛拟機不用等待守護線程執行完畢
-
如,背景記錄記錄檔,監控記憶體,垃圾回收等待
代碼
package ProcessThreadTest;
class DeamonClass implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
while(true)
System.out.println("守護程序--------------");
}
}
class NormalClass implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<100;i++) {
System.out.println("普通程序");
}
System.out.println("普通程序結束~~~~~");
}
}
public class DeamonTest {
public static void main(String[] args) {
Thread thread = new Thread(new DeamonClass());
thread.setDaemon(true);
thread.start();
new Thread(new NormalClass()).start();
}
}
運作結果:
線程同步
處理多線程問題時 , 多個線程通路同一個對象 , 并且某些線程還想修改這個對象 。這時候我們就需要線程同步 。線程同步其實就是一種等待機制 , 多個需要同時通路此對象的線程進入這個對象的等待池形成隊列, 等待前面線程使用完畢 , 下一個線程再使用。
由于同一程序的多個線程共享同一塊存儲空間 , 在帶來友善的同時,也帶來了通路 沖突問題 , 為了保證資料在方法中被通路時的正确性 , 在通路時加入 鎖機制synchronized , 當一個線程獲得對象的排它鎖 , 獨占資源 , 其他線程必須等待 , 使用後釋放鎖即可 . 存在以下問題 :
一個線程持有鎖會導緻其他所有需要此鎖的線程挂起 ;
在多線程競争下 , 加鎖 , 釋放鎖會導緻比較多的上下文切換 和 排程延時,引起性能問題 ;
如果一個優先級高的線程等待一個優先級低的線程釋放鎖 會導緻優先級倒置 , 引起性能問題 。
線程不安全舉例
不安全買票
代碼
package ProcessThreadTest;
class BuyTicket implements Runnable{
private int ticketNums = 10;
boolean flag = true;
public void buy() throws InterruptedException {
if(ticketNums<=0) {
flag = false;
return;
}
Thread.sleep(100); //模拟網絡延遲
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
public void run() {
while(flag) {
try {
buy();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class UnsafeBuyTIcket {
public static void main(String[] args) {
BuyTicket bt = new BuyTicket();
new Thread(bt,"A").start();
new Thread(bt,"B").start();
new Thread(bt,"C").start();
}
}
結果
不安全銀行
代碼
package ProcessThreadTest;
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"零花錢");
Drawing A = new Drawing(account,50,"A");
Drawing B = new Drawing(account,100,"B");
A.start();
B.start();
}
}
class Account{
int money;
String name;
Account(int a,String b){
money = a;
name = b;
}
}
class Drawing extends Thread{
Account account;
int drawingMoney;
String name;
public Drawing(Account account,int drawingMoney,String name) {
this.account = account;
this.drawingMoney = drawingMoney;
this.name = name;
}
public void run() {
if(account.money < drawingMoney) {
System.out.println(Thread.currentThread().getName() + "錢不夠");
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
System.out.println(name + "取了:"+ drawingMoney);
System.out.println(account.name + "餘額為" + account.money);
}
}
結果
不安全集合
代碼
package ProcessThreadTest;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for(int i = 0;i<10000;i++) {
new Thread( ()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
運作結果
可以看到list中的元素并不是一萬個,因為有的是同時新增的,其中的某些就被覆寫掉了。
同步方法
由于我們可以通過 private 關鍵字來保證資料對象隻能被方法通路 , 是以我們隻需 要針對方法提出一套機制 , 這套機制就是 synchronized 關鍵字 , 它包括兩種用法 : synchronized 方法 和synchronized 塊。
同步方法 : public synchronized void method(int args) {}
synchronized方法控制對 “對象” 的通路 , 每個對象對應一把鎖 , 每個synchronized方法都必須獲得調用該方法的對象的鎖才能執行 , 否則線程會阻塞 , 方法一旦執行 , 就獨占該鎖 , 直到該方法傳回才釋放鎖 , 後面被阻塞的線程才能獲得這個鎖 , 繼續執行。
缺陷 : 若将一個大的方法申明為synchronized 将會影響效率
同步方法,同步塊(synchronized)
Obj 稱之為同步螢幕
Obj 可以是任何對象 , 但是推薦使用共享資源作為同步螢幕
同步方法中無需指定同步螢幕 , 因為同步方法的同步螢幕就是this , 就是 這個對象本身 , 或者是 class
同步螢幕的執行過程
- 第一個線程通路 , 鎖定同步螢幕 , 執行其中代碼 。
- 第二個線程通路 , 發現同步螢幕被鎖定 , 無法通路。
- 第一個線程通路完畢 , 解鎖同步螢幕 。
- 第二個線程通路, 發現同步螢幕沒有鎖 , 然後鎖定并通路。
安全買票代碼
隻要把之前不安全的買票的buy方法加上synchronized修飾就可以保證同步了。
1.方法一
public synchronized void buy() throws InterruptedException {
if(ticketNums<=0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
安全取錢代碼
synchronized 預設鎖的是this方法,但這應該鎖Account類而不是Drawing類。
可以把幾個函數封裝在synchronized之中。
public void run() {
//account不是本對象,不能用synchronized方法。
synchronized(account) {
if(account.money < drawingMoney) {
System.out.println(account.name + " 錢不夠");
return;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
account.money -= drawingMoney;
System.out.println(name + "取了:"+ drawingMoney);
System.out.println(account.name + "餘額為" + account.money);
}
}
運作結果
安全list代碼
package ProcessThreadTest;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for(int i = 0;i<10000;i++) {
new Thread( ()->{
synchronized(list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
運作結果
JUC
安全類型的集合。
代碼
package ProcessThreadTest;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListTest {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for(int i = 0;i<10000;i++) {
new Thread( ()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
運作結果
死鎖
産生原因
多個線程各自占有一些共享資源 , 并且互相等待其他線程占有的資源才能運作 , 而導緻兩個或者多個線程都在等待對方釋放資源 , 都停止執行的情形 。某一個同步塊同時擁有 “ 兩個以上對象的鎖 ” 時 , 就可能會發生 “ 死鎖 ” 的問題。
解決方法:破壞任意條件
1. 互斥條件:一個資源每次隻能被一個程序使用。
2. 請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
3. 不剝奪條件 : 程序已獲得的資源,在末使用完之前,不能強行剝奪。
4. 循環等待條件 : 若幹程序之間形成一種頭尾相接的循環等待資源關系。
實驗代碼
package ProcessThreadTest;
public class DeadLockTest {
public static void main(String[] args) {
Makeup q1 = new Makeup(0,"A");
Makeup q2 = new Makeup(1,"B");
q1.start();
q2.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
Makeup(int Choice,String girlName){
this.choice = Choice;
this.girlName = girlName;
}
public void run() {
try {
makeup();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if(choice == 0) {
synchronized(lipstick) {
System.out.println(this.girlName + "獲得口紅鎖");
Thread.sleep(1000);
synchronized(mirror) {
System.out.println(this.girlName + "獲得鏡子鎖");
}
}
}else {
synchronized(mirror) {
System.out.println(this.girlName + "獲得鏡子鎖");
Thread.sleep(1000);
synchronized(lipstick) {
System.out.println(this.girlName + "獲得口紅鎖");
}
}
}
}
}
運作結果
Lock鎖
從JDK 5.0開始,Java提供了更強大的線程同步機制–通過顯式定義同步鎖對象來實作同步。同步鎖使用Lock對象充當java.util.concurrent.locks.Lock接口是控制多個線程對共享資源進行通路的工具。鎖提供了對共享資源的獨占通路,每次隻能有一個線程對Lock對象加鎖,線程開始通路共享資源之前應先獲得Lock對象 ReentrantLock類實作了Lock ,它擁有與synchronized相同的并發性和記憶體語義,在實作線程安全的控制中,比較常用的ReentrantL ock,可以顯式加鎖、釋放鎖。
synchronized 與 Lock 的對比
- Lock是顯式鎖(手動開啟和關閉鎖,别忘記關閉鎖)
- synchronized是隐式鎖,出了作用域自動釋放
- Lock隻有代碼塊鎖,synchronized有代碼塊鎖和方法鎖
- 使用Lock鎖,JVM将花費較少的時間來排程線程,性能更好。并且具有更好的擴充性(提供更多的子類)
- 優先使用順序: Lock > 同步代碼塊(已經進入了方法體,配置設定了相應資源)> 同步方法(在方法體之外)
不加鎖的情況
代碼
package ProcessThreadTest;
class MyLock implements Runnable{
int n = 10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
if(n > 0) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(n--);
}else {
break;
}
}
}
}
public class LockTest {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(myLock).start();
new Thread(myLock).start();
new Thread(myLock).start();
}
}
運作結果
加鎖的代碼
代碼
class MyLock implements Runnable{
int n = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true) {
try{
lock.lock();
if(n > 0) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(n--);
}else {
break;
}
}finally {
lock.unlock();
}
}
}
}
結果
線程協作
生産者消費者問題
假設倉庫中隻能存放一件産品 , 生産者将生産出來的産品放入倉庫 , 消費者将倉庫中産品取走消費 。
如果倉庫中沒有産品 , 則生産者将産品放入倉庫 , 否則停止生産并等待 , 直到 倉庫中的産品被消費者取走為止 。
如果倉庫中放有産品 , 則消費者可以将産品取走消費 , 否則停止消費并等待 , 直到倉庫中再次放入産品為止 。
這是一個線程同步問題 , 生産者和消費者共享同一個資源 , 并且生産者和消費者之間互相依賴 , 互為條件。對于生産者 , 沒有生産産品之前 , 要通知消費者等待 . 而生産了産品之後 , 又 需要馬上通知消費者消費。對于消費者 , 在消費之後 , 要通知生産者已經結束消費 , 需要生産新的産品以供消費. 在生産者消費者問題中 , 僅有synchronized是不夠的。synchronized 可阻止并發更新同一個共享資源 , 實作了同步 u synchronized 不能用來實作不同線程之間的消息傳遞 (通信)。
解決線程通信的方法
注意 : 均是Object類的方法 , 都隻能在同步方法或者同步代碼塊中使用,否則會抛出異常IllegalMonitorStateException
管程法
代碼實作
package ProcessThreadTest;
import java.util.Random;
public class ProductorConsumerTest {
public static void main(String[] args) {
SynContainer sc = new SynContainer();
new Productor(sc).start();
new Consumer(sc).start();
}
}
class toy{
private int id;
toy(int i){
setId(i);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
class Productor extends Thread{
SynContainer sc = new SynContainer();
Productor(SynContainer sc){
this.sc = sc;
}
public void run() {
Random random = new Random();
for(int i = 1;i<=100;i++) {
try {
Thread.sleep(random.nextInt(80));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.sc.push(new toy(i));
System.out.println("生産了第"+i+"隻雞"+",剩餘"+sc.getNums());
}
}
}
class Consumer extends Thread{
SynContainer sc = new SynContainer();
Consumer(SynContainer sc){
this.sc = sc;
}
public void run() {
Random random = new Random();
for(int i = 0;i<100;i++) {
try {
Thread.sleep(random.nextInt(100));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
toy t = this.sc.pop();
System.out.println("消費了第" +t.getId()+"隻雞"+",剩餘"+sc.getNums());
}
}
}
class SynContainer{
private toy[] toys = new toy[10];
private int nums = 0;
public int getNums() {
return nums;
}
public synchronized void push(toy t) {
if(nums >= 10) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
toys[nums] = t;
nums++;
this.notifyAll();
}
public synchronized toy pop() {
if(nums <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
nums--;
toy t = toys[nums];
this.notifyAll();
return t;
}
}
運作結果
信号燈法
代碼測試
package ProcessThreadTest;
public class ProductorConsumerTest1 {
public static void main(String[] args) {
TV tv = new TV();
new Watcher(tv).start();
new Player(tv).start();
}
}
class TV{
String program;
boolean flag = true;//true表示開始未表演狀态
public synchronized void play(String program) {
if(!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("演員表演了:"+program);
this.notifyAll(); //通知觀衆來看
this.program = program;
this.flag = !this.flag;
}
public synchronized void watch() {
if(flag) {
try {
this.wait();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("觀看了"+program);
this.notifyAll();
this.flag = !this.flag;
}
}
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
public void run() {
for(int i = 0;i<10;i++) {
this.tv.play("節目"+i);
}
}
}
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
public void run() {
for(int i = 0;i<10;i++) {
this.tv.watch();
}
}
}
運作結果
線程池
特點
背景:經常建立和銷毀、使用量特别大的資源,比如并發情況下的線程,對性能影響很大。 思路:提前建立好多個線程,放入線程池中,使用時直接擷取,使用完放回池中。 可以避免頻繁建立銷毀、實作重複利用。
好處: 提高響應速度;降低資源消耗;便于線程管理。
corePoolSize:核心池的大小
maximumPoolSize:最大線程數
keepAliveTime:線程沒有任務時最多保持多長時間後會終止
JDK 5.0起提供了線程池相關API:
ExecutorService
和
Executors
ExecutorService:真正的線程池接口。常見子類ThreadPoolExecutor
void execute(Runnable command) :執行任務/指令,沒有傳回值,一般用來執行Runnable <T> Future<T> submit(Callable<T> task):執行任務,有傳回值,一般又來執行 Callable
void shutdown() :關閉連接配接池
Executors:工具類、線程池的工廠類,用于建立并傳回不同類型的線程池
測試代碼
package ProcessThreadTest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.execute(new MyThread00());
service.shutdown();
}
}
class MyThread00 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<1;i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
運作結果
學習連結
https://kuangstudy.com/course