執行緒和行程的概念
程式:是為完成特定任務,用某種語言撰寫的一組指令的集合,即一段靜態的代碼,靜態物件,
行程:指在運行中的程式,程式一旦運行就是行程,同時行程也是是執行緒的容器,是系統進行資源分配和調度的單元,系統會在運行時為每個行程分配不同的記憶體取余,是資源分配的最小單元,是一個動態的程序:有它自身的產生,存在和消亡的程序,--生命周期
執行緒(thread)是作業系統能夠進行運算調度和執行的最小單元,它被包含在行程之中,是行程中的實際運作單位,一條執行緒指的是行程中一個單一順序的控制流,是一個程式內部的一條執行路徑,一個行程中可以并發多個執行緒,每條執行緒可以執行不同的任務,執行緒作為調度和執行的單位,每個執行緒擁有獨立的運行堆疊和程式計數器(pc)
多執行緒的創建
繼承Thread類
/**
* 創建多執行緒的方式一:
* 1.繼承Thread類
* 2.重寫Thread類中的run()方法
* 3.創建Thread類子類的物件
* 4.通過此物件呼叫start()
* 例子:遍歷100000以內的所有偶數
*/
public class ThreadTest {
public static void main(String[] args) {
MyThread m1 = new MyThread();
?
m1.start();
?
for (int i = 0; i < 100000; i++) {
if(i%2 == 0){
System.out.println(i+"main");
}
}
}
?
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i <100000 ; i++) {
if(i%2 == 0){
System.out.println(i);
}
}
}
}
會發現執行結果是相交的
…… 27250main 27252main 81768 81770 ……/** * 練習 * 創建倆個多執行緒,分別列印100以內的奇數和偶數 */ public class ThreadTest { public static void main(String[] args) { MyThread1 m1 = new MyThread1(); MyThread2 m2 = new MyThread2(); m1.start(); m2.start(); } } class MyThread1 extends Thread{ @Override public void run() { for (int i = 0; i <100 ; i++) { if(i%2 == 0){ System.out.println(Thread.currentThread().getName()+": "+i); } } } } class MyThread2 extends Thread{ @Override public void run() { for (int i = 0; i <100 ; i++) { if(i%2 == 1){ System.out.println(Thread.currentThread().getName()+": "+i); } } } }
運行結果
Thread-0: 0 Thread-1: 1 Thread-0: 2 Thread-1: 3 Thread-0: 4 Thread-1: 5 Thread-0: 6 Thread-1: 7 Thread-0: 8 Thread-1: 9 Thread-0: 10 Thread-1: 11 Thread-0: 12 Thread-1: 13 Thread-0: 14 Thread-1: 15 ……
實作Runnable介面
/** * 創建多執行緒的方式二: * 1.創建一個實作了Runnable介面的類 * 2.實作類去實作Runnable介面中的抽象方法:run() * 3.創建實作類的物件 * 4.將此物件作為引數傳遞到Thread類的構造器中,創建Thread類的物件 * 5.通過Thread類的物件呼叫start() */ public class RunnableTest { public static void main(String[] args) { Run run=new Run(); Thread thread1 = new Thread(run); Thread thread2 = new Thread(run); thread1.start(); thread2.start(); } } class Run implements Runnable{ @Override public void run() { for (int i = 0; i < 100000; i++) { System.out.println(Thread.currentThread().getName()+" "+i); } } }//Thread類原始碼 public class Thread implements Runnable { private Runnable target; public Thread(Runnable target) { this((ThreadGroup)null, target, "Thread-" + nextThreadNum(), 0L); } public void run() { if (this.target != null) { this.target.run(); } } ?
多執行緒實作視窗賣票的兩種方式
public class WindowsTest { public static void main(String[] args) { Windows windows1 = new Windows(); Windows windows2 = new Windows(); Windows windows3 = new Windows(); windows1.setName("視窗1"); windows2.setName("視窗3"); windows3.setName("視窗2"); windows1.start(); windows2.start(); windows3.start(); } } class Windows extends Thread{ ? private static int ticket = 100;//注意static ? @Override public void run() { while(true){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"賣票"+ticket); ticket--; } else { break; } } } ? }public class RunnableTest { public static void main(String[] args) { Run run=new Run(); Thread thread1 = new Thread(run); Thread thread2 = new Thread(run); Thread thread3 = new Thread(run); thread1.start(); thread2.start(); thread3.start(); } } class Run implements Runnable{ private int ticket=100;//這里不用加static @Override public void run() { while(true){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"賣票"+ticket); ticket--; }else { break; } } } }
多執行緒創建兩種方式的比較
開發中:優先選擇實作Runnable介面的方式
-
實作的方式沒有單繼承的局限性
-
實作的方式更適合來處理多個執行緒共享資料
聯系:Thread繼承了Runnable介面
相同點:都需要重寫run方法,將執行緒執行的邏輯寫在run方法中
JDK5.0新增執行緒創建方式
實作Callable介面
-
相比Runnable,可以有回傳值
-
方法可以拋出例外
-
支持泛型的回傳值
-
需要借助FutureTask類,比如獲取回傳結果
創建執行緒的方式三:
-
創建一個實作callable的介面
-
實作call方法,將此執行緒需要執行的操作宣告在call()中
-
創建callable介面實作類的物件
-
將此callable介面實作類的物件作為傳遞到FutureTask構造器中,創建FutureTask的物件
-
將FutureTask的物件作為引數傳遞到Thread類的構造器中,創建Thread物件并呼叫start方法
-
獲取callable中call方法的回傳值
public class CallableTest {
?
public static void main(String[] args) {
ThreadTests t = new ThreadTests();
FutureTask futureTask1 = new FutureTask(t);
new Thread(futureTask1).start();
try {
Object sum = futureTask1.get();
System.out.println("總合"+sum);
}catch (Exception e){
e.printStackTrace();
}
}
}
?
class ThreadTests implements Callable{
@Override
public Object call() throws Exception {
int num=0;
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
num += i;
}
}
return num;
}
}
執行緒池創建執行緒
優點:
-
提高回應速度(減少了創建新執行緒的時間)
-
降低資源消耗(重復利用執行緒池中執行緒,不需要每次都創建)
-
便于執行緒管理
public class ThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new NumberThreadTest());//適合Runnable介面
service.execute(new NumberThreadTest());
//service.submit()適合使用和callable
service.shutdown();//關閉執行緒池
}
}
class NumberThreadTest implements Runnable{
private static int ticket=100;
@Override
public void run() {
while(true){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}else {
break;
}
}
}
}
執行緒常用方法
yield();//釋放當前cpu的執行權join();//在執行緒a中呼叫執行緒b的join方法,執行緒a會陷入阻塞狀態直到執行緒b執行完畢stop();//強制執行緒生命期結束,不推薦使用boolean isAlive();//判斷執行緒是否還或者sleep(long timemilltime);//讓當前執行緒睡眠指定的milltime毫秒,在指定的milltime毫秒時間內,當前執行緒是阻塞狀態
以下三個方法必須在同步代碼塊或者同步方法中使用,并且呼叫者必須是同步代碼塊或同步方法中的同步監視器(同一把鎖)否則會出現IllegalMonitorStateException例外
屬于Object類中的方法
wait():一旦執行此方法,當前執行緒進入阻塞狀態,并釋放同步監視器notify():一旦執行此方法,就會喚醒被wait的一個執行緒,如果有多個執行緒被wait,則喚醒優先級高的notifyAll():喚醒所有執行緒
執行緒的優先級
MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
默認優先級都為5
如何獲取: 1. getPriority():獲取執行緒的優先級 2. setPriority(int p):設定執行緒的優先級
并不是優先級高就一定先被CPU執行,只能從概率上講更容易被CPU執行
執行緒的生命周期
-
新建
-
就緒
-
運行
-
阻塞
-
死亡


synchronized
操作共享資料的代碼,即為需要被同步的代碼
-
共享資料:多個執行緒共同操作的變數
-
同步監視器:鎖
-
任何一個類的物件都可以充當鎖
-
多個執行緒必須用同一把鎖
synchronized同步代碼塊解決執行緒安全問題
Runnable同步代碼塊解決執行緒安全的問題
/**
* 操作共享資料的代碼,即為需要被同步的代碼
* 共享資料:多個執行緒共同操作的變數
* 同步監視器:鎖
* 任何一個類的物件都可以充當鎖
* 多個執行緒必須用同一把鎖
*/
public class RunnableTest {
public static void main(String[] args) {
Run run=new Run();
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();
thread2.start();
thread3.start();
}
}
class Run implements Runnable{
private int ticket=100;
Object object=new Object();
@Override
public void run() {
?
while(true){
synchronized(object){//可以使用this充當鎖,this為當前物件
if(ticket>0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"賣票"+ticket);
ticket--;
}else {
break;
}
}
}
}
}
執行結果
Thread-0賣票100 Thread-0賣票99 Thread-2賣票98 Thread-1賣票97 Thread-1賣票96 Thread-1賣票95 Thread-2賣票94 Thread-2賣票93 Thread-2賣票92 Thread-2賣票91 Thread-2賣票90 Thread-2賣票89 Thread-2賣票88 Thread-2賣票87 Thread-2賣票86 Thread-2賣票85 Thread-0賣票84 Thread-0賣票83 Thread-0賣票82 Thread-0賣票81 Thread-0賣票80 Thread-0賣票79 Thread-0賣票78 Thread-0賣票77 Thread-0賣票76 Thread-0賣票75 Thread-0賣票74 Thread-0賣票73 Thread-0賣票72 Thread-0賣票71 Thread-0賣票70 Thread-0賣票69 Thread-0賣票68 Thread-0賣票67 Thread-0賣票66 Thread-0賣票65 Thread-0賣票64 Thread-0賣票63 Thread-0賣票62 Thread-0賣票61 Thread-0賣票60 Thread-2賣票59 Thread-2賣票58 Thread-2賣票57 Thread-2賣票56 Thread-2賣票55 Thread-2賣票54 Thread-2賣票53 Thread-2賣票52 Thread-2賣票51 Thread-2賣票50 Thread-2賣票49 Thread-2賣票48 Thread-2賣票47 Thread-2賣票46 Thread-2賣票45 Thread-2賣票44 Thread-2賣票43 Thread-1賣票42 Thread-1賣票41 Thread-1賣票40 Thread-1賣票39 Thread-1賣票38 Thread-1賣票37 Thread-1賣票36 Thread-1賣票35 Thread-1賣票34 Thread-1賣票33 Thread-1賣票32 Thread-1賣票31 Thread-1賣票30 Thread-1賣票29 Thread-1賣票28 Thread-1賣票27 Thread-1賣票26 Thread-2賣票25 Thread-2賣票24 Thread-2賣票23 Thread-2賣票22 Thread-2賣票21 Thread-2賣票20 Thread-2賣票19 Thread-2賣票18 Thread-2賣票17 Thread-2賣票16 Thread-2賣票15 Thread-2賣票14 Thread-2賣票13 Thread-2賣票12 Thread-2賣票11 Thread-2賣票10 Thread-2賣票9 Thread-2賣票8 Thread-2賣票7 Thread-2賣票6 Thread-2賣票5 Thread-2賣票4 Thread-2賣票3 Thread-0賣票2 Thread-0賣票1 ? Process finished with exit code 0
同步的方式解決了執行緒安全的問題,操作同步代碼時,只有一個執行緒參與,其他執行緒等待,相當于一個單執行緒的程序,效率低
Thread方式同步代碼塊解決執行緒安全的問題
public class ThreadTest {
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
m1.start();
m2.start();
m3.start();
}
}
class MyThread extends Thread{
private static int ticket=100;
Object object=new Object();
@Override
public void run() {
while(true){
synchronized(object){//不可以使用this充當鎖,這里this代表了m1,m2,m3三個物件
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"賣票:"+ticket);
ticket--;
}else {
break;
}
}
?
}
}
}
執行結果
Thread-0賣票:100 Thread-1賣票:100 Thread-2賣票:100 Thread-2賣票:97 Thread-1賣票:97 Thread-0賣票:97 Thread-2賣票:94 Thread-0賣票:94 Thread-1賣票:94 Thread-1賣票:91 Thread-2賣票:91 Thread-0賣票:90 Thread-2賣票:88 Thread-1賣票:88 Thread-0賣票:88 Thread-2賣票:85 Thread-0賣票:84 Thread-1賣票:84 Thread-2賣票:82 Thread-0賣票:82 Thread-1賣票:82 Thread-2賣票:79 Thread-0賣票:79 Thread-1賣票:78 Thread-2賣票:76 Thread-1賣票:76 Thread-0賣票:76 Thread-2賣票:73 Thread-1賣票:73 Thread-0賣票:73 Thread-0賣票:70 Thread-1賣票:70 Thread-2賣票:70 Thread-1賣票:67 Thread-0賣票:67 Thread-2賣票:66 Thread-2賣票:64 Thread-1賣票:64 Thread-0賣票:62 Thread-2賣票:61 Thread-1賣票:61 Thread-0賣票:61 Thread-1賣票:58 Thread-0賣票:58 Thread-2賣票:56 Thread-0賣票:55 Thread-1賣票:55 Thread-2賣票:55 Thread-0賣票:52 Thread-1賣票:52 Thread-2賣票:52 Thread-1賣票:49 Thread-2賣票:49 Thread-0賣票:49 Thread-2賣票:46 Thread-0賣票:46 Thread-1賣票:46 Thread-2賣票:43 Thread-0賣票:43 Thread-1賣票:43 Thread-2賣票:40 Thread-1賣票:40 Thread-0賣票:40 Thread-0賣票:37 Thread-2賣票:37 Thread-1賣票:37 Thread-2賣票:34 Thread-1賣票:34 Thread-0賣票:34 Thread-2賣票:31 Thread-0賣票:31 Thread-1賣票:31 Thread-1賣票:28 Thread-2賣票:28 Thread-0賣票:28 Thread-0賣票:25 Thread-2賣票:25 Thread-1賣票:25 Thread-1賣票:22 Thread-0賣票:22 Thread-2賣票:22 Thread-2賣票:19 Thread-0賣票:19 Thread-1賣票:19 Thread-2賣票:16 Thread-1賣票:16 Thread-0賣票:16 Thread-2賣票:13 Thread-0賣票:13 Thread-1賣票:13 Thread-2賣票:10 Thread-0賣票:10 Thread-1賣票:10 Thread-2賣票:7 Thread-0賣票:7 Thread-1賣票:7 Thread-2賣票:4 Thread-1賣票:4 Thread-0賣票:4 Thread-1賣票:1 Thread-2賣票:1 Thread-0賣票:1 ? Process finished with exit code 0 ?
會發現還是執行緒不安全,這是因為三個物件使用的鎖不是同一把鎖了
一定要注意給object 加上static 才行
static Object object=new Object();
加入后就會發現執行緒又安全了
或者使用 MyThread.class 充當鎖
Synchronized同步方法解決執行緒安全問題
使用同步方法解決執行緒安全問題
實作Runnable介面方式
public class SynchronizedMethod {
public static void main(String[] args) {
ThreadMethod threadMethod = new ThreadMethod();
Thread thread1 = new Thread(threadMethod);
Thread thread2 = new Thread(threadMethod);
Thread thread3 = new Thread(threadMethod);
thread1.start();
thread2.start();
thread3.start();
}
}
class ThreadMethod implements Runnable{
private int ticket=100;
public synchronized void show(){//這里用的鎖是this
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"賣票:"+ticket);
ticket--;
}
}
?
@Override
public void run() {
while(ticket>0){
show();
}
}
}
繼承Thread類方式
public class SynchronizedMethodThread {
public static void main(String[] args) {
ThreadMethod threadMethod1 = new ThreadMethod();
ThreadMethod threadMethod2 = new ThreadMethod();
ThreadMethod threadMethod3 = new ThreadMethod();
threadMethod1.start();
threadMethod2.start();
threadMethod3.start();
}
}
class ThreadMethod extends Thread{
private static int ticket=100;
?
public static synchronized void show(){//這里必須加上static,不加上static鎖使用的為this物件,這里創建了三個物件,加上static鎖使用的為ThreadMethod.class
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"賣票:"+ticket);
ticket--;
}
}
@Override
public void run() {
while(ticket>0){
show();
}
}
}
同步方法總結:
-
同步方法仍涉及到同步監視器,只是不需要我們顯示的宣告
-
非靜態的同步方法,同步監視器(鎖)是:this
-
靜態的同步方法,同步監視器(鎖)是:當前類本身
鎖方式解決執行緒安全問題
public class ReentrantLockTest { public static void main(String[] args) { Window window = new Window(); Thread thread1 = new Thread(window); Thread thread2 = new Thread(window); Thread thread3 = new Thread(window); thread1.start(); thread2.start(); thread3.start(); } } ? class Window implements Runnable{ private int ticket=100; private ReentrantLock reentrantLock=new ReentrantLock(); @Override public void run() { while (true){ reentrantLock.lock();//加鎖 try { if(ticket>0){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"賣票"+ticket); ticket--; }else { break; } }finally { reentrantLock.unlock();//釋放鎖 } } } }注意:如果用繼承Thread類的方式去使用lock
必須在 鎖前面加上static
private static ReentrantLock reentrantLock=new ReentrantLock();
Synchronized和鎖(Lock)方式的異同?
-
相同:兩者都可以解決執行緒安全問題
-
不同:Synchronized機制在執行完相應的同步代碼后,自動釋放同步監視器,Lock需要手動啟動同步(Lock()),同時結束也需要手動結束同步(unlock())
單例模式執行緒安全
class Bank{ private static Bank instance = null; private Bank(){} public static Bank getInstance(){ // synchronized(Bank.class){//方式一:效率稍差 // if (instance == null){ // instance=new Bank(); // } // return instance; // } //方式二:效率更高 if (instance == null){ synchronized(Bank.class){ if (instance == null){ instance=new Bank(); } } } return instance; } }死鎖
不同的執行緒分別占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖
死鎖案例
public class DeadLock { public static void main(String[] args) { StringBuffer s1=new StringBuffer(); StringBuffer s2=new StringBuffer(); new Thread(){ @Override public void run() { synchronized (s1){ s1.append("a"); s2.append("1"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s2){ s1.append("b"); s2.append("2"); System.out.println(s1); System.out.println(s2); } } } }.start(); ? new Thread(new Runnable() { @Override public void run() { synchronized (s2){ s1.append("c"); s2.append("3"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s1){ s1.append("d"); s2.append("4"); System.out.println(s1); System.out.println(s2); } } } }).start(); } }
多執行緒通信列題
public class CommunicationTest {
/**
* 實作兩個執行緒交替列印1-100
*/
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
Thread t3 = new Thread(test);
t1.start();
t2.start();
t3.start();
}
}
class Test implements Runnable{
private int number=1;
@Override
public void run() {
while (true){//while條件不能用number <= 100 因為這樣就沒有全鎖住操作共享資料的代碼,執行緒就不安全了
synchronized (this){ //也不能鎖住while 鎖住了就只有一個執行緒能進來操作了
if (number <= 100){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+number);
number++;
}else {
break;
}
}
}
}
}
package com.atguigu.thread;
?
public class CommunicationTest {
/**
* 執行緒通信的例子:實作兩個執行緒交替列印1-100
* 涉及到的方法:
* wait():一旦執行此方法,當前執行緒進入阻塞狀態,并釋放同步監視器
* notify():一旦執行此方法,就會喚醒被wait的一個執行緒,如果有多個執行緒被wait,則喚醒優先級高的
* notifyAll():喚醒所有執行緒
*/
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
Thread t3 = new Thread(test);
t1.start();
t2.start();
t3.start();
}
}
?
class Test implements Runnable {
private int number = 1;
?
@Override
public void run() {
synchronized (this) {
while (true) {
// while條件不能用number <= 100
// 因為這樣就沒有全鎖住操作共享資料的代碼,執行緒就不安全了
// 也不能鎖住while 鎖住了就只有一個執行緒能進來操作了
//除非使用以下方法來操作執行緒
//先阻塞當前執行緒,然后再次回圈時喚醒被阻塞的執行緒
this.notify();//喚醒執行緒,這個this必須與sychronized鎖住的物件相同
if (number <= 100) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
wait();//當前執行緒阻塞,并釋放同步監視器(鎖)
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
sleep()和wait()方法的異同
-
相同點:一旦執行,都會讓當前執行緒進入阻塞狀態
-
不同點:
-
宣告的位置不同,sleep宣告在Thread類中,wait宣告在Object類中,
-
是否釋放同步監視器,如果倆個方法都在同步代碼塊或同步方法中:sleep()不會釋放鎖,wait()會釋放鎖
-
呼叫的要求不同:sleep可以在任何需要的場景下呼叫,wait必須在同步代碼塊或同步方法中使用,
-
執行緒通信的應用:經典例題: 生產者消費者問題
/** * 執行緒通信的應用:經典例題: 生產者消費者問題 */ public class ProduceConsumerTest { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer p1 = new Producer(clerk); Consumer c1 = new Consumer(clerk); p1.start(); c1.start(); } } class Clerk{ private int product = 0; ? public void produce() { while(true){ ? synchronized (this){ this.notify(); if(product < 20){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } product++; System.out.println(Thread.currentThread().getName()+"生產者生產產品"+product); ? }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } ? public void consume() { while (true) { synchronized (this) { this.notify(); if (product > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } product--; System.out.println(Thread.currentThread().getName() + "消費者消費產品" + product); this.notify(); } else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } ? class Producer extends Thread{ ? private Clerk clerk; ? public Producer(Clerk clerk){ this.clerk=clerk; } @Override public void run() { clerk.produce(); } } ? class Consumer extends Thread{ ? private Clerk clerk; ? public Consumer(Clerk clerk){ this.clerk=clerk; } @Override public void run() { clerk.consume(); } }歡迎加入學習交流群:166543371
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/379537.html
標籤:java
上一篇:動態代理機制
