首頁 >Java >java教程 >Java知識點總結之多執行緒基礎

Java知識點總結之多執行緒基礎

WBOY
WBOY轉載
2022-11-15 17:24:221876瀏覽

本篇文章為大家帶來了關於java的相關知識,其中主要介紹了關於多執行緒的相關內容,同一時刻,可以執行多個執行緒。例如:使用迅雷可以同時下載多個文件,下面一起來看一下,希望對大家有幫助。

Java知識點總結之多執行緒基礎

推薦學習:《java影片教學

一、執行緒相關概念

1、程式:是為完成特定任務、用某種語言寫的一組指令的集合。簡單的說:就是我們寫的程式碼

2、進程:是指運行中的程序,例如我們使用QQ,就啟動了一個進程,作業系統就會為 該進程分配記憶體空間。當我們使用迅雷,又啟動了一個進程,作業系統將為迅雷分配新的記憶體空間。

進程是程式的一次執行過程,或是正在執行的程式。是動態過程:有它自身的產生、存在和消亡的過程。

3、線程:是進程創建的,是進程的實體,一個進程可以有多個線程,例如使用迅雷下載文件,迅雷相當於進程,下載的檔案相當於線程。

4、單執行緒:同一時刻,只允許執行一個執行緒

5、多執行緒:同一時刻,可以執行多個執行緒。例如:使用迅雷可以同時下載多個檔案。

6、並發:同一時刻,多個任務交替進行。單核心CPU實現的多任務就是並發。

7、並行:同一時刻,多個任務同時進行。多核心CPU可以實現並行,當任務較多時,並發和並行有可能同時發生。

二、執行緒基本上使用

建立執行緒有兩種方法:

1、繼承Thread類,重寫run方法。

2、實作Runnable接口,重寫run方法。

注意:Thread類別實作了Runnable介面。

(一) 繼承Thread類,重寫run方法

public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        Cat cat = new Cat();
        cat.start();
        System.out.println("主线程" + Thread.currentThread().getName());
        for (int i = 1; i <p>1、在繼承<code>Thread</code>類,重寫了<code>run ()</code>方法後,在<code>main</code>方法中需要建立對象,並呼叫<code>start()</code>方法,啟動執行緒。 </p><p>2、使用<code>start()</code>方法,會呼叫重寫的<code>run()</code>方法。 </p><p>3、如果<code>main</code>方法中,<code>start()</code>方法後面還有執行語句,且<code>run()</code>方法中也有執行語句,<code>main</code>執行緒中會啟動一個子執行緒<code>Thread-0</code>,主執行緒不會阻塞,主執行緒與子執行緒會交替執行。 </p><p>注意:如果主執行緒執行完畢,但子執行緒未執行完,行程不會結束,所有執行緒執行完畢後,行程自動結束。 </p><p>4、為什麼在主執行緒中使用<code>start()</code>方法去呼叫<code>run()</code>方法,而不是直接呼叫<code>run()</code>方法,因為<code>run()</code>方法是一個普通方法,沒有真正啟動線程,如果呼叫<code>run()</code>方法,就會將<code>run()</code>方法執行完畢後,再執行<code>main</code>方法剩餘語句,主執行緒就會阻塞。 </p><p>所以上面程式都運算結果是:</p><pre class="brush:php;toolbar:false">主线程main
主线程i=1你好1Thread-0主线程i=2你好2Thread-0主线程i=3你好3Thread-0主线程i=4你好4Thread-0主线程i=5你好5Thread-0

(二) 實作Runnable接口,重寫run方法

public class Thread02 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Thread thread = new Thread(dog);
        thread.start();
    }}class Dog implements Runnable{
    @Override    public void run() {
        int count = 0;
        while (true) {
            System.out.println("小狗汪汪叫" + (++count) + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10){
                break;
            }
        }
    }}

1、因為Runnable介面中沒有start()方法,所以需要代理

2、先在main方法中需要建立對象,然後建立一個Thread對象,將先前建立的物件傳入Thread,借助Thread,呼叫start()方法,建立執行緒。

3、建議使用Runnable,因為實作Runnable介面更適合多個執行緒共享一個資源的情況,避免了單一繼承的限制。

三、執行緒終止

(一)基本說明

1、執行完任務時會自動退出。

2、也可以使用變數控制run()方法退出終止執行緒。

(二)操作

1、如果一個執行緒中的run()方法內部是一個while(true){ },即一個無限迴圈

2、我們可以在執行緒內部,建立一個boolean屬性loop,讓loop = true,然後while(loop){}

3、再提供一個setLoop方法,這樣就可以在其他類別中呼叫setLoop方法,改變boolean值,來控制執行緒終止。

(三)程式碼示範

public class ThreadExit {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
        Thread.sleep(10000);
        t.setLoop(false);
    }}class T extends Thread{
    private boolean loop = true;
    private int times = 0;
    
    @Override    public void run() {
        while (loop){
            System.out.println("hello" + (++times));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }}

#四、執行緒常用方法

(一)第一組

1、setName:設定執行緒名稱,使其與參數name相同。

2、getName:返回该线程名称。

3、start:使该线程开始执行,JAVA虚拟机底层调用该线程的start0方法。

4、run:调用线程对象的run方法。

5、setPriority:更改线程的优先级。

6、getPriority:获取线程的优先级。

7、sleep:在指定的毫秒数内,让当前正在执行的线程休眠。

8、interrupt:中断线程。

注意事项:

1、start()底层会创建新的线程,调用run(),run()就是一个简单的方法调用,不会启动新线程

2、中断线程一般用于中断正在休眠的线程,并没有真正的结束线程,所以如果线程中每输出内容后,会调用Thread.sleep()进行休眠的话,我们可以调用interrupt()方法将其提前唤醒

代码演示:

public class ThreadMethod01 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setName("邱崇源");
        t.setPriority(1);
        t.start();
        for (int i = 0; i <h4>
<a id="_183"></a>(二)第二组</h4><p>1、<strong>yield</strong>:是一个<code>静态方法</code>,表示<code>线程的礼让</code>。让出<code>CPU</code>,让其他线程执行,但是<code>不一定成功</code>,因为这取决于CPU,如果CPU认为两个线程可以一起执行,则不进行礼让,所以<code>如果CPU的核数多,并且线程数少,礼让就会大概率失败</code>。</p><p>2、<strong>join</strong>:表示<code>线程的插队</code>,假如主线程与子线程正在交替运行,我们想<code>先让子线程执行</code>完毕,然后再让主线程执行,就可以使用线程插队,在主线程中,创建子线程对象,并调用<code>join</code>方法,可以实现线程插队,线程插队<code>一定会成功</code>,先<code>执行完插入线程任务后,再继续执行主线程</code>。</p><h4>
<a id="_186"></a>代码演示:</h4><pre class="brush:php;toolbar:false">public class ThreadMethod02 {
    public static void main(String[] args) throws InterruptedException {
        A a = new A();
        a.start();
        for (int i = 1; i <h2>
<a id="_219"></a>五、用户线程和守护线程</h2><p>1、<strong>用户线程</strong>:也叫<code>工作线程</code>,线程的任务执行完或通知方式结束。</p><p>2、<strong>守护线程</strong>:一般是<code>为工作线程服务</code>的,当<code>所有的用户线程结束</code>,<code>守护线程自动结束</code>。</p><h4>
<a id="_222"></a>应用场景:</h4><p>如果有两个线程,主线程运行结束,但子线程是无限循环。我们想让主线程结束的同时,子线程也结束,就需要让子线程变成守护线程。</p><p>在主线程中,创建子线程对象,并调用<code>setDaemon(true)</code>,让子线程变成守护线程。</p><p>注意:一定要放在<code>start方法之前</code>。</p><h4>
<a id="_226"></a>代码演示:</h4><pre class="brush:php;toolbar:false">public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();
        for (int i = 1; i <h2>
<a id="_254"></a>六、线程的生命周期</h2><h4>
<a id="1JDK__ThreadState__255"></a>1、JDK 中用 Thread.State 枚举表示了线程的几种状态</h4><p><img src="https://img.php.cn/upload/article/000/000/067/2235493b090cc60a6a3c1434c8852f59-0.png" alt="Java知識點總結之多執行緒基礎"></p><h4>
<a id="2_257"></a>2、线程状态转换图</h4><p><img src="https://img.php.cn/upload/article/000/000/067/cfbba58b413c41b084c9e0cab6295dfe-1.png" alt="Java知識點總結之多執行緒基礎"></p><h2>
<a id="_259"></a>七、线程的同步</h2><h4>
<a id="1_260"></a>1、应用场景:</h4><p>假如有100张票,有三个线程同时进入该方法买票,票就有可能超卖。所以我们需要线程同步机制,保证数据在同一时刻,最多有一个线程访问。</p><p>可以采取同步方法,在方法中加入<code>synchronized</code>关键字。</p><p>也可以采用同步代码块,<code>synchronized(对象){}</code>。</p><p>注意:<code>synchronized是非公平锁</code>,如果这次第一个线程访问了数据,那么下一次第一个线程也有可能访问到数据。</p><p>如果同步方法是<code>非静态</code>的,那么锁可以是this,也可以是其他对象,但要求是同一个对象。</p><p>例:<code>synchronized(this)</code>。</p><p>如果同步方法是<code>静态</code>的,锁为当前类本身。</p><p>例:<code>synchronized(类名:class)</code>。</p><h4>
<a id="2_270"></a>2、代码演示:</h4><pre class="brush:php;toolbar:false">public class SellTicket {
    public static void main(String[] args) {
        SellTicket02 sellTicket04 = new SellTicket02();
        Thread thread1 = new Thread(sellTicket04);
        Thread thread2 = new Thread(sellTicket04);
        Thread thread3 = new Thread(sellTicket04);
        thread1.start();
        thread2.start();
        thread3.start();
    }}class SellTicket02 implements Runnable {
    public static int ticket = 100;
    private boolean loop = true;

    public synchronized void sell() {
        if (ticket <h2>
<a id="_309"></a>八、线程的死锁</h2><h4>
<a id="1_310"></a>1、基本介绍</h4><p>多个线程都占用了对方的锁资源,但不肯相让,就导致了死锁,在编程中,一定要避免死锁的发生。</p><h4>
<a id="2_312"></a>2、发生场景:</h4><p>例如:A和B的面前都各有两道门,A的第一道门是o1,第二道门是o2。B的第一道门是o2,第二道门是o1。他们的面前有两把锁,一个是o1锁,一个是o2锁,假如A抢到了o1锁,B抢到了o2锁,但是他们只可打开各自的第一道门,第二道门都没有打开,那么他们都无法释放自己的锁资源,又不可能相让,因此发生了死锁。</p><h4>
<a id="3_314"></a>3、代码演示:</h4><pre class="brush:php;toolbar:false">public class DeadLock_ {
	public static void main(String[] args) { //模拟死锁现象 
		DeadLockDemo A = new DeadLockDemo(true); 
		A.setName("A 线程"); 
		DeadLockDemo B = new DeadLockDemo(false); 
		B.setName("B 线程");
		A.start(); 
		B.start(); 
	} }class DeadLockDemo extends Thread { 
	static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static 
	static Object o2 = new Object(); 
	boolean flag; 
	
	public DeadLockDemo(boolean flag) {//构造器 
		this.flag = flag; 
	}
	
	@Override 
	public void run() {  
		if (flag) { 
			synchronized (o1) { 
				System.out.println(Thread.currentThread().getName() + " 进入 1"); 
				synchronized (o2) { 
					System.out.println(Thread.currentThread().getName() + " 进入 2"); 
				} 
			} 
		} else {
			synchronized (o2) { 
				System.out.println(Thread.currentThread().getName() + " 进入 3"); 
				synchronized (o1) {  
					System.out.println(Thread.currentThread().getName() + " 进入 4"); 
				} 
			} 
		} 
	} }

九、释放锁

1、下面操作会释放锁

当前线程的同步方法,同步代码块执行结束。
当前线程在同步代码块,同步方法中遇到break,return
当前线程在同步代码块,同步方法中出现了未处理的错误或异常,导致异常结束。
当前线程在同步代码块,同步方法中执行的线程对象的wait方法,当前线程暂停,并释放锁。

2、 下面操作不会释放锁

线程执行同步代码块和同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁。
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。

推荐学习:《java视频教程

以上是Java知識點總結之多執行緒基礎的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除