Java的多線程1:線程的使用
概述
程序是線程的容器,線程共享程序的記憶體空間,是以線程之間彼此通信是比較容易的,而線程又有自己私有的記憶體位址,其他線程無法通路。了解程序和線程關系,可以看我另一篇部落格《程序與線程》
Java建立線程的兩種方式
繼承Thread類
public class ThreadDemo1 extends Thread {
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("目前執行的線程是" + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
ThreadDemo1 threadDemo1 = new ThreadDemo1();
ThreadDemo1 threadDemo2 = new ThreadDemo1();
threadDemo1.start();
threadDemo2.start();
}
}
執行結果是不确定的
實作Runnable
public class ThreadDemo2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
for (int j = 0;j < 1000; ++j){
System.out.println(i + "目前執行的線程是" + Thread.currentThread().getName());
}
}
}
public static void main(String[] args) {
ThreadDemo2 threadDemo1 = new ThreadDemo2();
ThreadDemo2 threadDemo2 = new ThreadDemo2();
Thread thread1 = new Thread(threadDemo1);
Thread thread2 = new Thread(threadDemo2);
thread1.start();
thread2.start();
System.out.println("目前線程是===>" + Thread.currentThread().getName());
}
主線程的名字為main,非主線程的名字是由虛拟機來指定的,同時,我們也可以為線程指定具體的名稱。
我們保證每個線程都能正常啟動,并不意味着它會按順序的執行,因為排程程式是無法保證它的執行次序的,同時,run()函數結束時,意味着該線程的任務完成了。
注意:調用線程要調用start,如果調用run,那僅僅是簡單的對象調用。
線程生命周期
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
建立狀态
線程對象建立後,就進入建立狀态 Thread thread = new Thread
就緒狀态
調用start()方法,線程進入就緒狀态,但并不意味着線程就立即執行,隻是說明此線程已經做好準備,随時等待CPU排程執行。
阻塞狀态
多個線程同時競争一個獨占鎖,其他未搶到鎖的線程,就進入阻塞狀态被放置到鎖池中,直到擷取到鎖,進入就緒狀态
等待狀态
該線程需要等待其他線程做出一些特定動作,通知或者是中斷,等待其被其他線程喚醒,像CountDownLatch就可以等待一個或者幾個線程結束。
逾時等待狀态
和等待狀态不同的是,它可以在指定的時間自行的傳回,sheep(long)函數就會讓線程進入逾時等待狀态,時間到了才會轉入就緒狀态。
運作狀态(Running)
CPU排程處于就緒狀态的線程時,這個線程才真正執行,進入運作狀态。
終止狀态
線程正常執行完畢後或提前強制性終止或出現異常,線程就要銷毀,釋放資源。
線程的方法調用
擷取線程基本資訊
public class ThreadDemo6 {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run(){
/*擷取線程唯一id辨別*/
long id = this.getId();
System.out.println("thread的ID==>" + id);
/*擷取線程名字*/
String name = this.getName();
System.out.println("thread的名字==>" + name);
/*擷取線程的優先級 預設5 1-10*/
int priority = this.getPriority();
System.out.println("thread的優先等級==>" + priority);
/*檢視目前線程是否為守護線程*/
boolean isDaemon = this.isDaemon();
System.out.println("thread是否為守護線程==>" + isDaemon);
/*檢視線程是否被中斷*/
boolean isInterrupted = this.isInterrupted();
System.out.println("thread是否被中斷==>" + isInterrupted);
}
};
thread.start();
}
執行結果
thread的ID==>11
thread的名字==>Thread-0
thread的優先等級==>5
thread是否為守護線程==>false
thread是否被中斷==>false
Thread.yield()
public class ThreadDemo1 implements Runnable {
protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public ThreadDemo1(){}
public ThreadDemo1(int countDown){
this.countDown = countDown;
}
public String status(){
return "#" + id + "(" + (countDown > 0 ? countDown : "stop!") + ")";
}
@Override
public void run() {
while (countDown-- > 0){
System.out.println(status() + " ");
Thread.yield();
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++){
new Thread(new ThreadDemo1()).start();
}
}
0(9)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(stop!)
1(9)#1(8)#1(7)#1(6)#1(5)#1(4)#1(3)#1(2)#1(1)#1(stop!)
2(9)#2(8)#2(7)#2(6)#2(5)#2(4)#2(3)#2(2)#2(1)#2(stop!)
這個是一個倒計時的任務,對Thread.yield()調用是對線程排程器的一種建議,它在聲明“我已經執行完生命周期中最重要的部分了,此刻正是切換給其他任務執行一段時間的大好時機”,說白就是自己按暫停鍵,讓出自己CPU的使用權限,轉為就緒狀态,至于下一次什麼時候能獲得CPU排程就不一定了,有時很快,有時得等上一會。
Thread.sleep
protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public ThreadDemo1(){}
public ThreadDemo1(int countDown){
this.countDown = countDown;
}
public String status(){
return "#" + id + "(" + (countDown > 0 ? countDown : "stop!") + ")";
}
@Override
public void run() {
try {
while (countDown-- > 0){
System.out.println(status());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++){
new Thread(new ThreadDemo1()).start();
}
}
Thread.sleep(long)将使“正在執行的任務“中止執行給定的時間(暫停執行)并且讓出CPU使用權,這個語句相當于說在接下來的1秒内,你都不要叫我,我想睡一會,1秒睡眠時間過後,它自動轉為就緒狀态,但CPU不一定馬上執行這個睡醒的線程,這要取決于是否搶到CPU時間片段。值得注意的是如果sleep和yield上下文被加鎖了,它們依然使用鎖,不會去釋放。而sleep與yield最大的不同是,yield不會讓線程進入等待狀态,隻是把線程轉為就緒狀态,并把CPU執行機會讓步給優先級相同或者更高的線程,而sleep能控制具體交出CPU的使用時間。
Thread.currentThread()
public class ThreadDemo2 extends Thread {
static {
System.out.println("靜态塊執行的線程===>" + Thread.currentThread().getName());
}
{
System.out.println("非靜态塊執行的線程是====>" + Thread.currentThread().getName());
System.out.println("this.getName()1=====>" + this.getName());
}
public ThreadDemo2(){
System.out.println("構造方法内執行的線程====>" + Thread.currentThread().getName());
System.out.println("this.getName()2=====>" + this.getName());
}
@Override
public void run() {
System.out.println("目前執行的線程為====>" + Thread.currentThread().getName());
System.out.println("this.getName()3=====>" + this.getName());
}
public static void main(String[] args) {
ThreadDemo2 threadDemo2 = new ThreadDemo2();
threadDemo2.start();
}
靜态塊執行的線程===>main
非靜态塊執行的線程是====>main
this.getName()1=====>Thread-0
構造方法内執行的線程====>main
this.getName()2=====>Thread-0
目前執行的線程為====>Thread-0
this.getName()3=====>Thread-0
currentThread傳回的是目前正在執行線程對象的引用,它與this.getName()有明顯的不同,執行靜态塊,非靜态塊,構造方法的線程是main,而非ThreadDemo2,在執行run()方法的才是執行個體化的線程threadDemo2。是以在目前執行的Thread未必就是Thread本身。
isAlive()
public class ThreadDemo3 extends Thread {
@Override
public void run(){
System.out.println("執行執行====" + this.isAlive());
}
public static void main(String[] args) {
ThreadDemo3 threadDemo3 = new ThreadDemo3();
System.out.println("begin===>" + threadDemo3.isAlive());
threadDemo3.start();
System.out.println("end==>" + threadDemo3.isAlive());
}
begin===>false
end==>true
執行執行====true
isAlive()檢測線程是否處于活動狀态,活動狀态傳回true
setPriority()
優先級設定,優先級高的線程越容易擷取CPU使用權,
public class ThreadDemo4 {
public static void main(String[] args) {
for (int i = 0; i < 5; ++i){
Thread1 thread1 = new Thread1();
thread1.setPriority(6);
Thread2 thread2 = new Thread2();
thread2.setPriority(4);
thread2.start();
thread1.start();
}
}
class Thread1 extends Thread{
@Override
public void run(){
for (int i = 0; i < 100000; ++i){
System.out.println("+++++++++++++");
}
}
class Thread2 extends Thread{
@Override
public void run(){
for (int i = 0; i < 100000; ++i){
System.out.println("--------------");
}
}
+++++++++++++
...
CPU會将資源盡量讓給優先級高的線程
setDaemon()
守護線程,也有人叫後天線程,我們建立出來的線程預設都是前台線程,在使用上來說,守護線程和前台線程是沒啥差別,差別在于程序結束,當一個程序中的所有前台線程都結束時,無論這個程序中的守護線程是否還在運作都要強制将他們結束。也就是說前台線程都結束了,守護線程也會自動銷毀,它是為其他線程提供便利而存在的。
/rose與jack/
public class ThreadDemo5 {
public static void main(String[] args) {
Rose rose = new Rose();
Jack jack = new Jack();
/*設定為守護線程必須線上程未啟動之前*/
jack.setDaemon(true);
rose.start();
jack.start();
}
class Rose extends Thread{
@Override
public void run(){
for (int i = 0; i < 5; ++i){
System.out.println("rose: let me go!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("成功跳水");
}
class Jack extends Thread{
@Override
public void run(){
while (true){
System.out.println("jack:you jump! i jump!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
rose: let me go!
jack:you jump! i jump!
成功跳水
jack守護着rose,jack是守護線程,當rose跳水後,jack認為自己也沒有活着的必要了,也自己銷毀了,但注意一點是這當中還有一個第三者main,需要main也運作完jack線程才會銷毀。
join()
這個方法可以協調多個線程同步運作,多線程運作本身是設計異步運作的,但在程式運作業務中,有可能線程A的計算需要線程B的傳回結果,這就需要他們執行各自任務時要有先後,join就需要協調這些線程同步運作。
private static boolean isFinish = false;
public static void main(String[] args) {
Thread download = new Thread(){
@Override
public void run(){
System.out.println("下載下傳圖檔中.....");
for (int i = 1; i <= 100; ++i){
System.out.println("下載下傳進度" + i + "%");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("圖檔下載下傳完畢");
isFinish = true;
}
};
Thread show = new Thread(){
@Override
public void run(){
System.out.println("開始顯示圖檔...");
try {
download.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!isFinish){
throw new RuntimeException("圖檔下載下傳出錯");
}
System.out.println("圖檔正常展示。。。");
}
};
download.start();
show.start();
}
下載下傳圖檔中.....
開始顯示圖檔...
下載下傳進度1%
下載下傳進度2%
下載下傳進度100%
圖檔下載下傳完畢
圖檔正常展示。。。
show調用join會使show無限阻塞,直到down線程銷毀為止,它和sleep最大的差別是join會釋放鎖,而sleep不會。
涉及到jmm記憶體模型,線程安全等,後面在介紹
原文位址
https://www.cnblogs.com/dslx/p/12664004.html