一、實現(xiàn)多線程
1刽沾、繼承Thread類
方法名 | 說明 |
---|---|
void run() | 在線程開啟后本慕,此方法將被調(diào)用執(zhí)行 |
void start() | 使此線程開始執(zhí)行,Java虛擬機(jī)會調(diào)用run方法() |
run()方法和start()方法的區(qū)別侧漓?
run()
:封裝線程執(zhí)行的代碼,直接調(diào)用监氢,相當(dāng)于普通方法的調(diào)用
start()
:啟動線程布蔗;然后由JVM調(diào)用此線程的run()方法-
實現(xiàn)步驟
- 定義一個類MyThread繼承Thread類
- 在MyThread類中重寫run()方法
- 創(chuàng)建MyThread類的對象
- 啟動線程
//實現(xiàn)多線程
class MyThread extends Thread {
@Override
public void run() {
// 當(dāng)前線程
String name = Thread.currentThread().getName();
System.out.println(name);
//代碼就是線程在開啟之后執(zhí)行的代碼
for (int i = 0; i < 3; i++) {
System.out.println("線程開啟了" + i);
}
}
}
// 使用
public class Demo {
public static void main(String[] args) {
// 當(dāng)前線程
String name = Thread.currentThread().getName();
System.out.println(name);
//創(chuàng)建一個線程對象
MyThread t1 = new MyThread();
//表示的僅僅是創(chuàng)建對象,用對象去調(diào)用方法,并沒有開啟線程
t1.run();
//開啟一條線程
t1.start();
}
}
2、實現(xiàn)Runnable接口
方法名 | 說明 |
---|---|
Thread(Runnable target) | 分配一個新的Thread對象 |
Thread(Runnable target, String name) | 分配一個新的Thread對象 |
-
實現(xiàn)步驟
- 定義一個類MyRunnable實現(xiàn)Runnable接口
- 在MyRunnable類中重寫run()方法
- 創(chuàng)建MyRunnable類的對象
- 創(chuàng)建Thread類的對象浪腐,把MyRunnable對象作為構(gòu)造方法的參數(shù)
- 啟動線程
// 實現(xiàn)Runnable接口
class MyRunnable implements Runnable{
@Override
public void run() {
// 當(dāng)前線程
String name = Thread.currentThread().getName();
System.out.println(name);
//線程啟動后執(zhí)行的代碼
for (int i = 0; i < 3; i++) {
System.out.println( "Runnable:" + i);
}
}
}
public class Demo {
public static void main(String[] args) {
// 當(dāng)前線程
String name = Thread.currentThread().getName();
System.out.println(name);
//創(chuàng)建了一個參數(shù)的對象
MyRunnable mr = new MyRunnable();
//創(chuàng)建了一個線程對象,并把參數(shù)傳遞給這個線程.
//在線程啟動之后,執(zhí)行的就是參數(shù)里面的run方法
Thread t1 = new Thread(mr);
//開啟線程
t1.start();
}
}
3.實現(xiàn)Callable接口
方法名 | 說明 |
---|---|
V call() | 計算結(jié)果纵揍,如果無法計算結(jié)果,則拋出一個異常 |
FutureTask(Callable<V> callable) | 創(chuàng)建一個 FutureTask议街,一旦運(yùn)行就執(zhí)行給定的 Callable |
V get() | 如有必要泽谨,等待計算完成,然后獲取其結(jié)果 |
-
實現(xiàn)步驟
- 定義一個類MyCallable實現(xiàn)Callable接口
- 在MyCallable類中重寫call()方法
- 創(chuàng)建MyCallable類的對象
- 創(chuàng)建Future的實現(xiàn)類FutureTask對象特漩,把MyCallable對象作為構(gòu)造方法的參數(shù)
- 創(chuàng)建Thread類的對象吧雹,把FutureTask對象作為構(gòu)造方法的參數(shù)
- 啟動線程
- 再調(diào)用get方法,就可以獲取線程結(jié)束之后的結(jié)果涂身。
/ 實現(xiàn)Callable接口
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
// 當(dāng)前線程
String name = Thread.currentThread().getName();
System.out.println(name);
for (int i = 0; i < 3; i++) {
System.out.println("Callable:" + i);
}
//返回值就表示線程運(yùn)行完畢之后的結(jié)果
return "答應(yīng)";
}
}
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 當(dāng)前線程
String name = Thread.currentThread().getName();
System.out.println(name);
//線程開啟之后需要執(zhí)行里面的call方法
MyCallable mc = new MyCallable();
//可以獲取線程執(zhí)行完畢之后的結(jié)果.也可以作為參數(shù)傳遞給Thread對象
FutureTask<String> ft = new FutureTask<>(mc);
//創(chuàng)建線程對象
Thread t1 = new Thread(ft);
//開啟線程
t1.start();
String s = ft.get();
System.out.println(s);
}
}
4.三種實現(xiàn)方式的對比
- 實現(xiàn)Runnable雄卷、Callable接口
- 好處: 擴(kuò)展性強(qiáng),實現(xiàn)該接口的同時還可以繼承其他的類
- 缺點: 編程相對復(fù)雜蛤售,不能直接使用Thread類中的方法
- 繼承Thread類
- 好處: 編程比較簡單丁鹉,可以直接使用Thread類中的方法
- 缺點: 可以擴(kuò)展性較差妒潭,不能再繼承其他的類
二、多線程的使用
1.設(shè)置和獲取線程名稱
方法名 | 說明 |
---|---|
void setName(String name) | 將此線程的名稱更改為等于參數(shù)name |
String getName() | 返回此線程的名稱 |
Thread currentThread() | 返回對當(dāng)前正在執(zhí)行的線程對象的引用 |
static void sleep(long millis) | 使當(dāng)前正在執(zhí)行的線程停留(暫停執(zhí)行)指定的毫秒數(shù) |
final int getPriority() | 返回此線程的優(yōu)先級 |
final void setPriority(int newPriority) | 更改此線程的優(yōu)先級線程默認(rèn)優(yōu)先級是5揣钦;線程優(yōu)先級的范圍是:1-10 |
void setDaemon(boolean on) | 將此線程標(biāo)記為守護(hù)線程雳灾,當(dāng)運(yùn)行的線程都是守護(hù)線程時,Java虛擬機(jī)將退出 |
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 30; i++) {
try {
// 不同線程睡眠時間不同冯凹,觀察控制臺打印
String name = Thread.currentThread().getName();
if (name.equals("1")){
Thread.sleep(10);
}else{
Thread.sleep(20);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
MyRunnable mr = new MyRunnable();
//線程是有默認(rèn)名字的,格式:Thread-編號
// 設(shè)置線程名
Thread t1 = new Thread(mr,"1");
Thread t2 = new Thread(mr,"2");
//設(shè)置優(yōu)先級: 1 - 10 默認(rèn)值:5
t1.setPriority(10);
t2.setPriority(1);
t1.start();
t2.start();
}
}
2.線程鎖:synchronized
- 安全問題出現(xiàn)的條件
- 是多線程環(huán)境
- 有共享數(shù)據(jù)
- 有多條語句操作共享數(shù)據(jù)
- 同步代碼塊格式:
synchronized(任意對象) {
多條語句操作共享數(shù)據(jù)的代碼
}
synchronized(任意對象):就相當(dāng)于給代碼加鎖了佑女,任意對象就可以看成是一把鎖
public class MyThread extends Thread {
private static int ticketCount = 100;
private static Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj){ //就是當(dāng)前的線程對象.
if(ticketCount <= 0){
//賣完了
break;
}else{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketCount--;
System.out.println(Thread.currentThread().getName() + "在賣票,還剩下" + ticketCount + "張票");
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("窗口一");
t2.setName("窗口二");
t1.start();
t2.start();
}
}
2.線程鎖:ReentrantLock
方法名 | 說明 |
---|---|
ReentrantLock() | 創(chuàng)建一個ReentrantLock的實例 |
-
加鎖解鎖方法
方法名 說明 void lock() 獲得鎖 void unlock() 釋放鎖
public class Ticket implements Runnable {
//票的數(shù)量
private int ticket = 100;
private Object obj = new Object();
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
if (ticket <= 0) {
//賣完了
break;
} else {
ticket--;
System.out.println(Thread.currentThread().getName() + "在賣票,還剩下" + ticket + "張票");
}
lock.unlock();
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket ticket = new Ticket();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
3..生產(chǎn)者消費者
-
Object類的等待和喚醒方法
方法名 說明 void wait() 導(dǎo)致當(dāng)前線程等待,直到另一個線程調(diào)用該對象的 notify()方法或 notifyAll()方法 void notify() 喚醒正在等待對象監(jiān)視器的單個線程 void notifyAll() 喚醒正在等待對象監(jiān)視器的所有線程