首頁  >  文章  >  Java  >  什麼是進程與執行緒?

什麼是進程與執行緒?

零下一度
零下一度原創
2017-06-25 10:18:251795瀏覽

一 概述

1.什麼是進程?

進程是一個相對獨立的執行單位。

2.什麼是執行緒?

進程的一部分,進程中實際的任務執行者,必須依附於進程。執行緒對行程的依賴主要體現在:

  • 執行緒無法脫離進程開啟,必須在進程開啟的前提下開啟。

  • 執行緒有時必須從進程中取得資料。

3.執行緒與行程的差異?

執行緒與進程是兩個相對的概念,一個物件相對於它擁有的執行單位稱為進程,從自身所屬的上級執行者來看,又 被稱作線程。

4.多線程的設計目的、用途、意義

# CUP在任何一個時間點都只能執行一個線程,多線程的本質是多個任務高速交替執行。如果多個執行緒間不存在資料交換,可以單獨執行,採用多執行緒並不能減少總的執行時間。

多線程設計的主要目的不是為了提高執行速度,而是相對平均地執行每一個線程,不致使某一個線程長時間持有CPU時間片, 其他執行緒長時間處於等待狀態。由於CPU時間片在多個執行緒間切換迅速,超出了人類感官所能察覺的範圍,所以感覺多個任務都是執行。

例如,當多個人造訪同一個網站,每一個人都需要5分鐘,如果不採用多線程,同時只允許一個人進入網站,其他多數人 都要等5分鐘,使用者體驗很差。這是採用多線程,一個人進入以後,CPU轉向其他用戶,讓其他用戶陸續進入,用戶體驗#就提高了,儘管總的執行時間並沒有減少。

5.CPU調度模式

  • #分時調度模式:系統平均地為各個執行緒分配CPU時間片。

  • 搶佔式調度模式:各個執行緒搶奪CPU時間片,CPU時間片在執行緒間不均勻分配。

二執行緒建立

# 1.Java SE API 提供了兩種建立執行緒的方式:

  • 實作Runnable接口,將實作類別的物件作為參數傳入Thread的建構器中。

  • 直接繼承Thread類別。

2.無論採用哪一種方式,需要執行的任務都必須放在run方法中。

3.兩種建立方式的差異:

⑴Java採用單繼承,即一個類別只能繼承一個父類,而允許一個類別實作多個接口,採用繼承Thread的方式來建立線程,就讓本類失去了唯一的一次繼承機會。

⑵實作資源共享的方式不同

  • # 首先需要明確的一點,透過繼承Thread建立執行緒的方式也可以實作資源共享,只是由於透過new關鍵字建立的多個執行緒是不同的對象,那麼共用資源只能來自外部,通常透過建構器注入。

  • 而透過實作Runnable介面的方式建立線程,可以利用同一個實作類別物件建立多個線程,實作了資源共享,共享資源來自執行緒內部。

4.採用實作Runnable介面的方式建立線程,不僅保留了唯一的繼承機會,而且在實作資源共享的操作相對簡單,所以一般採用該方式建立執行緒。

5.透過繼承Thread的方式實作資源共享:

提供共享資源的外部類別

package com.test.thread.extendsThread;public class MyClass {public int count;

}

Thread執行緒​​子類別

package com.test.thread.extendsThread;public class MyThread extends Thread {private MyClass obj;public MyThread() {super();
    }public MyThread(MyClass obj) {super();this.obj = obj;
    }

    @Overridepublic void run() {
        System.out.println("obj=" + obj);while (true) {synchronized (obj) {if (obj.count > 0) {try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "----当前数量=" + obj.count--);
                } elsereturn;
            }
        }

    }

}

測試類別

package com.test.thread.extendsThread;import org.junit.Test;import com.test.thread.synchronizedTest.demo02.MyTestRunnable;import net.sourceforge.groboutils.junit.v1.MultiThreadedTestRunner;import net.sourceforge.groboutils.junit.v1.TestRunnable;public class ThreadExtendsTest {/** * JUnit单元测试不支持多线程测试,使用GroboUtils进行多线程测试(导入架包)
     * 
     * @throws Throwable     */@Testpublic void test01() throws Throwable {
        MyClass obj = new MyClass();
        obj.count = 10;
        MyThread myth01 = new MyThread(obj);
        MyThread myth02 = new MyThread(obj);
        MyThread myth03 = new MyThread(obj);
        MyTestRunnable t01 = new MyTestRunnable(myth01);
        MyTestRunnable t02 = new MyTestRunnable(myth02);
        MyTestRunnable t03 = new MyTestRunnable(myth03);
        TestRunnable[] tr = new TestRunnable[3];
        tr[0] = t01;
        tr[1] = t02;
        tr[2] = t03;
        MultiThreadedTestRunner mttr = new MultiThreadedTestRunner(tr);
        mttr.runTestRunnables();
    }// 放在主线程中测试public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.count = 10;
        MyThread t01 = new MyThread(obj);
        MyThread t02 = new MyThread(obj);
        MyThread t03 = new MyThread(obj);
        t01.setName("t01");
        t02.setName("t02");
        t03.setName("t03");
        t01.start();
        t02.start();
        t03.start();
    }
}

 

三 线程生命周期

1.什么是线程的生命周期?

由不同阶段构成的线程从出生到死亡的整个过程,叫做线程的生命周期。

2.线程生命周期的意义

了解线程的生命周期能够更好地掌握线程的运行情况,比如线程的就绪状态,意味着不是调用start方法之后,线程立即执行。

3.生命周期的几个阶段:

  • 出生状态:线程创建完成,尚未开启前的状态。

  • 就绪状态:调用start方法开启线程,线程尚未运行的状态。

  • 运行状态:线程获取CPU时间片执行时的状态。

  • 休眠状态:线程调用sleep方法后进入指定时长的休眠状态,时间结束进入就绪状态。

  • 等待状态:监听对象在线程内部调用wait方法后,线程失去对象锁,进入等待状态。

  • 阻塞状态:线程发出输入或者输出请求后进入阻塞状态。

  • 死亡状态:run方法执行完毕,线程死亡。

四 线程的加入

一个线程A在另一个线程B内部调用join方法,B线程中止,A线程开始执行,A线程执行完毕,B线程才开始执行。

五 线程优先级

线程优先级设定了线程获取CPU时间片的概率,仅仅是一种概率,不能保证优先级高的线程一定优先获得CPU时间片。

线程优先级分为10个等级,从1-10,数值越大,优先级越高,通过setProprity(int)方法设置。

六 线程礼让

Thread.yield,线程礼让只是通知当前线程可以将资源礼让给其他线程,并不能保证当前线程一定让出资源。

七 同步机制

1.什么是线程同步机制?

使得同一资源同一时刻只能有一个线程访问的安全机制,即一个线程访问完毕,其他线程才能访问。

2.同步机制的目的

由于目标资源同一时刻只有一个线程访问,解决了线程安全问题。

3.什么是线程安全问题?

⑴线程安全问题产生条件

  • 多线程并发访问。

  • 存在可修改的共享数据。

⑵第一个线程获取了共享数据,操作结束前,第二个线程修改了该数据,导致第一个线程运算时采用的不是获取时的数据。

4.同步机制解决线程安全问题的原理

synchronized(共享对象){ 修改共享数据的代码 }

上述操作给修改共享数据的代码加了一把对象锁。任何一个对象只有一把对象锁,线程只有获得了对象锁才能访问加锁的资源。一个线程获取了对象锁,执行加锁的代码,执行完毕,归还对象锁,其他线程开始争夺对象锁,访问资源。

5.类锁

synchronized关键字加到静态方法上时,形成类锁,执行该方法上必须获取类锁。

类锁与对象锁是两种不同的锁,允许一个线程持有类锁,另一个线程持有对象锁。

6.synchronized关键字

synchronized关键字加在成员方法,该方法成为同步成员方法,由于一个对象只有一把对象锁,一个线程访问了一个同步成员方法,其他线程不能访问其他同步成员方法。

同步方法不可以被继承,同步方法在子类中失去同步机制。

7.判断条件的设置

在同步机制中,如果同步代码的执行需要满足一定条件,那么将判断条件放在锁内,保证当前获取了锁的线程在执行同步代码时满足执行条件。如果放在锁外,有可能出现当前线程获取了锁以后不满足执行条件的情况。

不存在线程安全问题的做法:

public void run() {
        System.out.println("obj=" + obj);while (true) {synchronized (obj) {if (obj.count > 0) {try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "----当前数量=" + obj.count--);
                } elsereturn;
            }
        }

    }

如果將判斷條件obj.count>0放在while語句中,可能出現某個執行緒進入while語句時count為1,滿足條件,進入,等待取得物件鎖定。目前持有物件鎖的執行緒執行完畢,count變成0,等待執行緒取得物件鎖定,在count=0的情況下執行同步區塊,判斷條件失效。

 

八 死鎖

1.什麼是死鎖?

線程A需要多把鎖,線程B持有A缺少的鎖,缺少A持有的鎖,由於線程在獲取到全部的鎖之前不會釋放持有的鎖,這使得線程A與線程B陷入僵持,整個進程處於停滯狀態。

2.怎麼避免死鎖?

減少同步機制中鎖的數目,盡量避免同一把鎖出現在多處。

九 守護執行緒

1.使用者執行緒?

一般情況下建立的線程都是使用者線程,即該線程未被明確設定為守護線程,未在守護線程內部建立。

2.主執行緒屬於使用者執行緒。

3.什麼是守護線程?

運行在後台、為使用者執行緒提供服務的執行緒。

4.守護執行緒建立

使用者執行緒呼叫setDaemon(true)方法,或在守護執行緒內部建立執行緒。

5.守護執行緒的作用

守護執行緒用於為使用者執行緒提供服務,例如垃圾回收器。

6.JVM在所有的使用者執行緒執行完畢後終止,無論此時守護執行緒是否執行完畢。

7.守護執行緒運行在後台,所有使用者執行緒結束後,自動結束。

十wait與sleep方法比較

# 1.存在範圍

#wait方法是Object級的,即java中的任何一個物件都擁有該方法,像toString一樣。

  • sleep方法只在Thread及其子類別中存在。

  • 2.作用

    #sleep使目前執行緒休眠,釋放CPU時間片,不會釋放持有的鎖。

wait用於線程間通信,由物件管理所有以該物件為鎖的全部執行緒。在同步程式碼中由鎖定物件調用,使目前執行緒釋放
  • 所持有的物件鎖定。

  • 3.使用方法

#sleep方法是一個靜態方法,直接透過Thread調用,Thread.sleep。

  • 用在同步程式碼中,由鎖定物件呼叫。

    4.相關方法
  • #obj.notify():隨機喚醒物件監聽器上的一個線程,該線程進入就緒狀態,一旦獲得物件鎖與CPU時間片,從等待處接著執行,######不是重新進入run方法或同步程式碼中。 ##################obj.notifyAll():喚醒物件監聽器上所有的等待線程,使它們全部進入就緒狀態。 ######

十一 ThreadLocal

1.线程局部变量,为每一个线程提供一个变量的副本,使得各个线程相对独立地操作变量,避免线程安全问题。

2.首先必须明确一点,ThreadLocal.get()获取的变量副本必须手动传入:

ThreadLocal.set(Object obj)

初次获取时,判断线程局部变量中是否保存有变量副本,如果没有则手动传入,在该线程中下次获取的就是初次传入的对象。

3.ThreadLocal的目的是保证在一个线程内部,一次创建,多次获取。

4.基本原理:
      将初次传入的变量与线程绑定,线程不变,变量不变。

十二 GroboUtils多线程测试

   1.JUnit测试不支持多线程,GroboUtils提供了对多线程测试的支持,使用时需要导入架包。

    2.几个比较重要的类:

  • TestRunnable:实现了Runnable接口,run方法中运行的是runTest方法,runTest方法是一个抽象方法。

  • MultiThreadedTestRunner:负责管理并开启多个线程。

    3.测试步骤

⑴继承TestRunnable,实现其中的抽象方法runTest,将需要运行的代码放入该方法中。通常为子类定义一个有参构造方法,方法形参为需要测试的线程,在runTest方法中调用测试线程的run方法,从而将将需要执行的代码注入runTest方法中。

⑵创建测试线程数组,将需要测试的TestRunnable实现类传入其中:

TestRunnable[] tr=new TestRunnable[len];

⑶根据测试线程数组创建线程管理与运行对象并开启多线程:

MultiThreadedTestRunner mttr=new MultiThreadedTestRunner(tr);
mttr.runTestRunnables();

以上是什麼是進程與執行緒?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn