面試題:
JVM是多線程程序嗎?至少有幾條線程..
jvm是多線程的,
至少有2條線程...
有主線程,main..執行這些代碼,能夠被Jvm識別
垃圾回收線程
在執行程序的時候,一些對象會被Jvm釋放掉,原因,
它開啓了垃圾回收線程,裏面GC:垃圾回收器(回收一些沒有更多引用的對象或者變量...)
代碼示例
public class Demo {
public static void main(String[] args) {
System.out.println("hello");
new Object();
new Object();
new Object();
new Object();
new Object();
// ......n個
System.out.println("world");
}
}
如何實現多線程程序呢?
要實現多線程程序,需要開啓進程,
開啓進程,是需要創建系統資源,但是Java語言不能創建系統資源
只有C/C++可以創建系統資源, 利用c語言創建好的系統資源實現
Java提供了一個類:Thread類
實現多線程程序的步驟:
1)將該類聲明爲Thread的子類
2)該子類應重寫 Thread類的 run方法
3)在主線程創建該自定義線程類的對象
並行和併發(高併發:MyBatis --->IBatis:半自動化)
前者邏輯上的同時,指的是同一個時間段內
後者物理上的同時,指的是同一個時間點
代碼示例
public class ThreadDemo {
public static void main(String[] args) {
// 創建MyThread類對象
// MyThread my = new MyThread() ;
// 當前Thread類有一個run public void run()
// my.run();
// System.out.println("-------------------");
// my.run();
// 執行線程不是run方法 ,run方法的調用相當於一個普通方法的調用
/*
* public void start()使該線程開始執行;Java虛擬機調用該線程的 run 方法。 結果是兩個線程併發地運行
*/
MyThread t1 = new MyThread();
t1.start();
// IllegalThreadStateException:非法狀態異常,同一個線程只能被執行一次
// t1.start();
MyThread t2 = new MyThread();
t2.start();
}
}
public final void setDaemon(boolean on) :true時,表示爲守護線程將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。(守護線程不會立即結束掉,它會執行一段時間在結束掉)
該方法必須在啓動線程前調用。
代碼示例
package thread;
public class ThreadDemo2 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
myThread.setName("張飛");
myThread2.setName("關羽");
myThread.setDaemon(true);// 設置爲守護線程
myThread2.setDaemon(true);// 設置爲守護線程
myThread.start();
myThread2.start();
Thread.currentThread().setName("劉備");
for (int x = 0; x < 5; x++)
System.out.println(Thread.currentThread().getName());
}
}
跟線程的優先級相關的方法:public final int getPriority()返回線程的優先級。
public final void setPriority(int newPriority)更改線程的優先級
線程存在一個默認優先級
public static final int MAX_PRIORITY 10 最大優先級
public static final int MIN_PRIORITY 1 最小優先級
public static final int NORM_PRIORITY 5 默認優先級
代碼示例
package thread;
public class ThreadDemo3 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();
myThread.setName("林青霞");
myThread2.setName("王祖賢");
myThread3.setName("林志穎");
myThread.setPriority(1);
myThread2.setPriority(5);
myThread3.setPriority(10);
System.out.println(myThread3.getPriority());
myThread2.start();
myThread.start();
myThread3.start();
}
}
public final void join():等待該線程終止 interruputedException 中斷異常分別創建三個子線程,讓第一個子線程執行之後,調用join()等待該線程終止,再執行t2,t3線程
代碼示例
package thread;
public class ThreadDemo4 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();
myThread.setName("李淵");
myThread2.setName("李世民");
myThread3.setName("李元霸");
myThread.start();
try {
myThread.join();
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
myThread2.start();
myThread3.start();
}
}
睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以“減慢線程”。當線程睡眠時,它
入睡在某個地方,在甦醒之前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。
線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因爲Java規範不保證合理的輪換。
睡眠的實現:調用靜態方法。
try {
Thread.sleep(123);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:爲了讓其他線程有機會執行,可以將Thread.sleep()的調用放線程run()之內。這樣才能保證該線程執行過程中會睡眠。
例如,在前面的例子中,將一個耗時的操作改爲睡眠,以減慢線程的執行。可以這麼寫:
public void run() {
for(int i = 0;i<5;i++){
// 很耗時的操作,用來減慢線程的執行
// for(long k= 0; k <100000000;k++);
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace(); .
}
System.out.println(this.getName()+" :"+i);
}
}
public static void sleep(long millis):線程睡眠 指定是時間毫秒值
throws InterruptedException兩個區別? public final void stop() ;強迫線程停止執行。 不會執行了 (過時了),方法能使用的
public void interrupt()中斷線程。 表示中斷線程的一種狀態 面試題 區別? wait(): wait()調用的,立即釋放鎖 (同步鎖/Lock鎖)
sleep(): 線程睡眠,調用不會釋放鎖
代碼示例
package thread;
import java.util.Date;
public class MyThread extends Thread {
public MyThread() {
super();
// TODO Auto-generated constructor stub
}
public MyThread(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
// super.run();
for (int x = 0; x < 50; x++) {
try {
Thread.sleep(1000);
System.out.println(getName() + "的線程睡眠1毫秒");
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
System.out.println(getName() + ":" + x);
}
}
}
package thread;
public class ThreadDemo4 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread2 = new MyThread();
myThread.setName("李淵");
myThread2.setName("李世民");
myThread.start();
myThread2.start();
}
}
需求:某電影院出售某些電影票(復聯3,紅高粱....),有三個窗口同時進行售票(100張票),請您設計一個程序,模擬電影院售票兩種方式:
繼承
接口
第一種方式:繼承類Thread
代碼示例
package org.westos_01;
//SellTicket線程
public class SellTicket extends Thread {
// 爲了不讓外界更改這個類中的數據
// private
// 定一個票數
// 爲了讓每一個線程共同使用一個數據,此數據應該被static修飾
// private int tickets = 100 ;
private static int tickets = 100;
@Override
public void run() {
// 爲了模擬電影賣票(模擬一直有票)
// 死循環
// st1,st2,st3都有執行這個裏面的方法
// st1 100
// st2 100
// st3 100
while (true) {
if (tickets > 0) {
System.out.println(getName() + "正在出售第" + (tickets--) + "張票");
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
//創建三個子線程,分別代碼三個窗口
SellTicket st1 = new SellTicket() ;
SellTicket st2 = new SellTicket() ;
SellTicket st3 = new SellTicket() ;
//設置線程名稱
st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3");
//啓動線程
st1.start();
st2.start();
st3.start();
}
}
第二種方式 實現接口Runnable
代碼示例
package org.westos_02;
public class SellTicket implements Runnable {
// 定義100張票
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
}
}
}
}
package org.westos_02;
//第二種方式實現
//給繼續加一些內容(延遲操作實現)
public class SellTicketDemo {
public static void main(String[] args) {
// 創建資源類對象(共享資源類對象/目標對象(target))
SellTicket st = new SellTicket();
// 創建線程類對象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 啓動線程
t1.start();
t2.start();
t3.start();
}
}
爲了模擬更真實的場景,加入延遲操作(讓我們線程睡100毫秒)
程序的設計是好的,但是結果有一些問題
1)同一張票被賣了多次
CPU的執行有一個特點(具有原子性操作:最簡單最基本的操作)
2)出現了0或者負票
(延遲操作+線程執行的隨機性)
代碼示例
package org.westos_03;
public class SellTicket implements Runnable {
//定義100張票
private int tickets = 100 ;
/* @Override
public void run() {
while(true) {
//t1先進來 t2
if(tickets>0) {
//爲了模擬更真實的場景(網絡售票有延遲的),稍作休息
try {
//t1睡 t2睡
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"張票");
//100張票
//爲什麼會出現同一張票被賣多次?
*//**
* 出現同票的原因:CPU的執行有一個特點(具有原子性操作:最簡單最基本的操作)
* t1線程進來,睡完了,100張票
* 原子性操作:記錄以前的值
* 接着tickets-- :票變成99張票
* 在馬上輸出第99張票的時候,t2/t3進來,直接輸出所記錄的之前的票數
* 出現:
* 窗口1正在出售第100張票
* 窗口3正在出售第99張票
* 窗口2正在出售第99張票
* 原子性操作
}
}
}*/
@Override
public void run() {
while (true) {
try {
// t1睡 t2睡
Thread.sleep(100); // 延遲操作
} catch (InterruptedException e) {
e.printStackTrace();
}
if (tickets > 0) {
//t1,t2,t3 三個線程執行run裏面代碼
//爲了模擬更真實的場景(網絡售票有延遲的),稍作休息
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"張票");//0
/**
* 理想狀態:
* t1正在出售第3張票
* t3正在出售第2張票
* t2正在出售第1張票
* ...
* 負票
* t1出售第0張票 (延遲操作+線程執行的隨機性)
* t3正在出售-1張票
*
*/
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
// 創建資源類對象(共享資源類/目標對象)
SellTicket st = new SellTicket();
// 創建線程類對象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 啓動線程
t1.start();
t2.start();
t3.start();
}
}
通過剛纔的這個程序,有安全問題(同票還有負票)如何解決多線程的安全問題?
校驗一個多線程程序是否有安全隱患的前提條件:
1)當前程序是多線程環境
2)有共享數據
3)有多條語句對共享數據進行操作
看當前案例是否有多線程的安全問題:
1)是否是多線程環境 是
2)是否有共享數據 是
3)是否有多條語句對共享數據進行操作 是
現在就需要解決安全問題:
1)多線程環境 不能解決
2)對共享數據進行優化 不能解決
3)在多條語句對共享數據進行操作這一環進行解決
解決方案:就是將多條語句對共享數據操作的代碼,用同步代碼塊包起來
格式:
synchronized(鎖對象){
針對多條語句對共享數據操作代碼;
}
鎖對象:肯定一個對象,隨便創建一個對象(匿名對象)
給剛纔的這個程序加入了同步代碼塊,但是鎖對象使用的是匿名對象(每一個線程進來都有自己各自的鎖),還是沒有解決!
鎖對象:所有線程公用一把鎖
代碼示例
package demo;
public class SellTicket implements Runnable {
private int tickets = 100;
private Object object = new Object();
@Override
public void run() {
while (true) {
synchronized (object) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
}
}
}
}
}
package demo;
public class SellTicketDemo {
public static void main(String[] args) {
// 創建資源類對象(共享資源類對象/目標對象(target))
SellTicket sellTicket = new SellTicket();
// 創建線程類對象
Thread thread = new Thread(sellTicket, "窗口1");
Thread thread2 = new Thread(sellTicket, "窗口2");
Thread thread3 = new Thread(sellTicket, "窗口3");
// 啓動線程
thread.start();
thread2.start();
thread3.start();
}
}
舉例:火車上上廁所(鎖對象:將它看成是門的開和關)
synchronized(鎖對象){
多條語句對共享數據進行操作的代碼;
}
注意:
鎖對象:一定要使用同一把鎖(所有線程只能公用一把鎖)
鎖對象:任何的Java類(引用類型)
代碼示例
package org.westos_05;
public class SellTicket implements Runnable {
// 定義100張票
private int tickets = 100;
private Object obj = new Object();
private Demo d = new Demo();
@Override
public void run() {
while (true) {
// t1,t2,t3
synchronized (d) { // 門的開和關
// t1進來,門會關掉
// t2進來,門關掉
// t3進來,門關掉
if (tickets > 0) {
try {
// 0.1
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
// 窗口1正在出售第100張票
// 窗口2正在出售第99張票
// 窗口3正在出售98張票
// ....
// 雖然加入延遲操作,但是因爲有synchronized,所以不會出現0票或者負票了
}
}
}
}
}
class Demo {
}
package org.westos_05;
/**
* 舉例:
* 火車上上廁所(鎖對象:將它看成是門的開和關)
*
* synchronized(鎖對象){
* 多條語句對共享數據進行操作的代碼;
* }
*
*注意:
* 鎖對象:一定要使用同一把鎖(所有線程只能公用一把鎖)
* 鎖對象:任何的Java類(引用類型)
*
*
*/
public class SellTicketDemo {
public static void main(String[] args) {
//創建資源類對象(共享資源類對象/目標對象)
SellTicket st = new SellTicket() ;
//創建線程類對象
Thread t1 = new Thread(st, "窗口1") ;
Thread t2 = new Thread(st ,"窗口2") ;
Thread t3 = new Thread(st, "窗口3") ;
//啓動線程
t1.start();
t2.start();
t3.start();
}
}
同步方法
當同步方法爲靜態(static)時
private synchronized static void SellTicket(){}
synchronized(類名.class);
當同步方法爲非靜態時
private synchronized void SellTicker(){}
synchronized(this);
代碼示例
package demo;
public class SellTicket implements Runnable {
private static int tickets = 100;
private static int x = 100;
@Override
public void run() {
while (true) {
if (x % 2 == 0) {
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"+" x:"+x--);
}
}
}
else {
sellTicket();
}
}
}
//靜態同步方法
/*private synchronized static void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"+" x:"+x--);
}
}*/
//非靜態同步方法
private synchronized void sellTicket() {
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"+" x:"+x--);
}
}
}
線程安全的類 StringBuffer
線程安全的集合
單列集合 Vector
雙列集合 Hashtable
代碼示例
package org.westos_06;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
public class ThreadDemo {
public static void main(String[] args) {
// 線程安全的類StringBuffer Vector<V> Hashtable<K,V>
StringBuffer sb = new StringBuffer();
Vector<String> vector = new Vector<String>();
Hashtable<String, String> hm = new Hashtable<String, String>();
// Vector<String>它是線程安全的集合,但是還是不習慣使用這個集合,通過ArrayList集合:線程不安全的類
List<String> arrayList = new ArrayList<String>(); // 線程不安全的類
ArrayList<String> arrayList2 = new ArrayList<String>();
// public static <T> List<T> synchronizedList(List<T> list)
// 返回指定列表支持的同步(線程安全的)列表
List list = Collections.synchronizedList(arrayList); // 線程安全的方法
List list2 = Collections.synchronizedList(arrayList2); // 線程安全的方法
}
}j
jdk5.0以後,java提供了一個具體的鎖: 接口:Lock
private Lock lock=new ReentrantLock();
lock.lock();//顯示獲取鎖
lock.unlock();//釋放鎖
代碼示例
package org.westos_07;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SellTicket implements Runnable {
// 定義票
private int tickets = 100;
// Object obj = new Object();
// Jdk5.0以後,java提供了一個具體的鎖: 接口:Lock
private Lock lock = new ReentrantLock(); // 顯示獲取鎖的前提,一定要創建Lock接口對象
@Override
public void run() {
while (true) {
try { // try...finally
lock.lock(); // 獲取鎖 syncrhonized(obj)
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
}
} finally {// 釋放鎖
if (lock != null) {
lock.unlock();
}
}
}
}
}
死鎖
簡單描述就是比如說有兩個鎖,第一個鎖中包含着第二個鎖,第二個鎖中也包含着第一個鎖,第一個鎖執行到其代碼塊中有第二個鎖的時候,它會等第二個鎖執行完畢釋放掉後才能執行此處的代碼,必須等待第二個鎖執行完畢,但是線程搶到CPU主動權後執行第二把鎖對應的代碼塊時,執行到其代碼塊中有第一把鎖的地方,必須等第一把鎖執行完畢釋放掉第一把鎖後纔會執行此處代碼,所以第二把鎖在等待第一把鎖執行完畢,就這樣,第一把鎖在等待第二把鎖,第二把鎖也在等待第一把鎖,互相等待啊!!!,就這樣一直等啊等,等啊等,就一直不執行,一直等着,就變成了死鎖。
代碼示例
package org.westos_08;
public class DieLock extends Thread {
// 聲明一個成員變量
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
// 重寫run方法
@Override
public void run() {
if (flag) {
synchronized (MyLock.objA) {
System.out.println("if ObjA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
} else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
/**
* 第一種情況: if ObjA else objB
*
* 第二種情況 else objB if ObjA
*
* 第三種情況: 理想狀態 else objB else objA if ObjA if objB
*
* if ObjA if objB else objB else objA
*
*
*/
}
package org.westos_08;
/**
* 自定義兩個鎖對象
*
*/
public class MyLock {
// 兩個鎖對象分別是objA 和objB
public static final Object objA = new Object();
public static final Object objB = new Object();
}
package org.westos_08;
//測試類
//解決了多線程安全問題,但是還是有些問題:
//1)執行效率低
//2)會產生死鎖
//兩個或兩個以上的線程,在執行的過程中出現互相等待的情況,就叫做死鎖!
public class DieLockDemo {
public static void main(String[] args) {
// 創建線程了對象
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);
// 啓動線程
dl1.start();
dl2.start();
}
}
分析:Student類: 資源類
SetThread:設置學生的數據(生產者線程)
GetThread:獲取(輸出)學生數據(消費者線程)
StudentDemo:測試類
需求:SetThread線程給學生對象進行賦值,再通過消費者線程輸出該學生數據,設計這樣一個程序!
null----0 按照剛纔的思路,發現有一個問題,的數據null---0
解決方案:線程死鎖的注意事項:要保證生產者線程和消費者線程是針對同一個對象進行操作的!
在主線程main中創建一個學生對象,將這個學生對象通過構造方法傳給每一個線程
代碼示例
package org.westos_09;
public class Student {
String name;
int age;
}
package org.westos_09;
//生產者線程
public class SetThread implements Runnable {
private Student student;
public SetThread(Student s) {
this.student = s;
}
@Override
public void run() {
// 設置學生數據
student.name = "高圓圓";
student.age = 27;
}
}
package org.westos_09;
//消費者線程
public class GetThread implements Runnable {
private Student student;
public GetThread(Student s) {
this.student = s;
}
@Override
public void run() {
// 輸出該學生數據
System.out.println(student.name + "----" + student.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();
}
}
分析:
Student類: 資源類
SetThread:設置學生的數據(生產者線程)
GetThread:獲取(輸出)學生數據(消費者線程)
StudentDemo:測試類
需求:SetThread線程給學生對象進行賦值,在通過消費者線程輸出該學生數據,設計這樣一個程序!
null----0 按照剛纔的思路,發現有一個問題,的數據null---0
解決方案:線程死鎖的注意事項:要保證生產者線程和消費者線程針對同一個對象進行操作的!
在外部創建一個學生對象,將這個學生對象通過構造方法傳入到各個線程中
需求:消費者線程,和生產者線程加入循環操作,改進
又有問題:
1)同一個人的姓名和年齡出現多次
2)姓名和年齡不符
爲什麼?
1)CPU的一點點時間片,在某一個時間點,足夠它執行很多次
2)線程具有隨機性
解決方案:
1)是否是多線程環境 是
2)是否有共享數據 是
3)是否有多條語句對共享數據進行操作 有
同步機制(同步代碼塊/同步方法)
開發中,使用synchronized(Lock鎖也可以)同步代碼塊將多條語句對共享數據的操作包起來!
代碼示例
package org.westos_10;
//生產者線程
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 (x % 2 == 0) {
s.name = "高圓圓";
s.age = 27;
} else {
s.name = "張楊";
s.age = 28;
}
x++;
}
}
}
}
package org.westos_11;
//消費者線程
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) { // true==!false s.flag==false 說明沒有數據 !flag
try {
s.wait();// 和網絡編程裏面的accept()一樣 都屬於阻塞式方法
// 消費線程等待,等待生產者線程生產數據
} catch (InterruptedException e) {
e.printStackTrace();
}
} // !s.flag==false s.flag==true 說明有數據,有數據則輸出數據
System.out.println(s.name + "----" + s.age);// 高圓圓---27
// 輸出之後就沒有數據了
s.flag = flase;
// 通知生產者線程,讓其趕緊生產數據
s.notify(); // 喚醒生產者線程
}
}
}
}
package org.westos_11;
//消費者線程
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) { // true==!false s.flag==false 說明沒有數據 !flag
try {
s.wait();// 和網絡編程裏面的accept()一樣 都屬於阻塞式方法
// 消費線程等待,等待生產者線程生產數據
} catch (InterruptedException e) {
e.printStackTrace();
}
} // !s.flag==false s.flag==true 說明有數據,有數據則輸出數據
System.out.println(s.name + "----" + s.age);// 高圓圓---27
// 輸出之後就沒有數據了
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();
}
}
ThreadGroup 線程組
線程組:表示一個線程的集合。此外,線程組也可以包含其他線程組
代碼示例
package org.westos_12;
/**
* 線程組:表示一個線程的集合。此外,線程組也可以包含其他線程組
*
* 線程池(某個線程執行完畢,反覆利用線程對象)
*
*/
public class ThreadGroupDemo {
public static void main(String[] args) {
// 獲取線程組的名稱
//
// method1();
// 如何給多個線程設置一個線程組名稱呢?
method2();
}
private static void method2() {
// public ThreadGroup(String name)構造一個新線程組
ThreadGroup tg = new ThreadGroup("main-新的線程組");
MyThread my = new MyThread();
// Thread(ThreadGroup group, Runnable target, String name)
Thread t1 = new Thread(tg, my, "線程1");
Thread t2 = new Thread(tg, my, "線程2");
// 直接獲取線程組名稱
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
}
private static void method1() {
MyThread my = new MyThread();
// 創建線程類對象
Thread t1 = new Thread(my, "線程1");
Thread t2 = new Thread(my, "線程2");
// public final ThreadGroup getThreadGroup()返回該線程所屬的線程組
ThreadGroup tg1 = t1.getThreadGroup();
ThreadGroup tg2 = t2.getThreadGroup();
// public final String getName():返回線程組的名稱
System.out.println(tg1.getName()); // main
System.out.println(tg2.getName());// main
// 所有的線程它默認的線程組名稱:main(主線程)
System.out.println(Thread.currentThread().getThreadGroup().getName());// main
}
}
package org.westos_12;
public class MyThread implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
線程池 ExecutorService
線程池:多個線程執行完畢,它會重新回到線程池中,等待被利用,不會變成垃圾!
和線程池有關的類
類 Executors: 一種工廠類
方法:
和線程池的創建有關係
public static ExecutorService newFixedThreadPool(int nThreads)
創建一個可重用固定線程數的線程池
ExecutorService:可以執行異步任務
創建一個線程池,執行接口中的方法
提交:Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)提交一個返回值的任務用於執行,返回一個表示任務的未決結果的 Future
Future:接口
Future 表示異步計算的結果
線程池調用完畢可以關閉的
void shutdown():關閉之前,會提交剛纔的任務
代碼示例
public class ExceutorsDemo {
public static void main(String[] args) {
// 創建一個線程池
ExecutorService pool = Executors.newFixedThreadPool(2);// 創建一個線程池中包含了2條線程
// 提交和Runnable接口的方法或者Callable(提交任務)
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
// pool-1-thread-2 :線程池-池數-線程類對象的描述-編號(從1開始)
// 關閉線程池
pool.shutdown();
}
}
package org.westos_13;
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
Callable
call()方法
多線程第三種實現方式:
前提:自定義類實現Callable接口
1)創建線程池對象: Executors 裏面的那個方法,返回的是ExecutorsService
2) 然後調用ExecutorsService裏面的提交任務的方法:
<T> Future<T> submit(Callable<T> task)提交一個返回值的任務用於執行
3)關閉線程池
代碼示例
public class ExecutorsDemo {
public static void main(String[] args) {
// 創建線程池對象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 提交任務
pool.submit(new MyCallable());
pool.submit(new MyCallable());
// 關閉線程池
pool.shutdown();
}
}
package org.westos_14;
import java.util.concurrent.Callable;
//Callable的泛型的類型它是call方法的返回值類型
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
for (int x = 0; x < 100; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
return null;
}
}
需求:利用線程來計算數字之和
代碼示例
package org.westos_15;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 分別計算每個線程的求和!
*
*/
public class ExecutorsTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 創建線程池對象
ExecutorService pool = Executors.newFixedThreadPool(2);
// 提交任務
Future<Integer> f1 = pool.submit(new MyCallable(100));
Future<Integer> f2 = pool.submit(new MyCallable(200));
// V get():獲取結果
Integer i1 = f1.get();
Integer i2 = f2.get();
System.out.println(i1);
System.out.println(i2);
// 關閉線程池
pool.shutdown();
}
}
package org.westos_15;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
// 定義個變量
private int number;
public MyCallable(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
// 定義最終結果變量
int sum = 0;
for (int x = 1; x <= number; x++) {
sum += x;
}
return sum;
}
}