Java多線程之Runable與Thread


Java多線程是Java開發中的基礎內容,但是涉及到高並發就有很深的研究可做了。

最近看了下《Java並發實戰》,發先有些地方,雖然可以理解,但是自己在應用中很難下手。

  所以還是先回顧一下基礎知識:

Java中的線程

  線程的相關知識,了解操作系統的基本都能有所了解。

  線程有5中狀態,基本變化如圖所示:

如何在Java代碼中創建線程

  眾所周知,Java創建線程有兩種方式:

  1 實現Runable接口

  2 繼承Thread類

  那么這兩種方式有什么區別呢?

  1 Runable屬於接口,所以可以有多個實現;Thread只有一個。

  2 實現Runable的線程類,可以被多個線程實例共享數據。

  舉個簡單的例子,火車站售票處一共有3個售票口,但是只剩下5張票:

  如果單純使用Thread實現3個售票口的售票過程:

package com.imooc.test;
class MyThread extends Thread{
    
    private int ticketsCount = 5;
    private String name;
    
    public MyThread(String name){
        this.name = name;
    }
    
    @Override
    public void run() {
        while(ticketsCount > 0){
            ticketsCount--;
            System.out.println(name+" 賣了一張票,還剩下:"+ticketsCount);
        }
    }
}
public class TicketsTestThread {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("窗口1");
        MyThread mt2 = new MyThread("窗口2");
        MyThread mt3 = new MyThread("窗口3");
        
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

  執行結果如下:

窗口1 賣了一張票,還剩下:4
窗口2 賣了一張票,還剩下:4
窗口1 賣了一張票,還剩下:3
窗口1 賣了一張票,還剩下:2
窗口1 賣了一張票,還剩下:1
窗口1 賣了一張票,還剩下:0
窗口2 賣了一張票,還剩下:3
窗口2 賣了一張票,還剩下:2
窗口2 賣了一張票,還剩下:1
窗口2 賣了一張票,還剩下:0
窗口3 賣了一張票,還剩下:4
窗口3 賣了一張票,還剩下:3
窗口3 賣了一張票,還剩下:2
窗口3 賣了一張票,還剩下:1
窗口3 賣了一張票,還剩下:0

  可以看到每個線程擁有自己的5張票,其實是重復了!

  那么如果使用Runnable,則不會出現這種情況:

package com.imooc.test;
class MyRunnable implements Runnable{
    
    private int ticketsCount = 5;
    
    @Override
    public void run() {
        while(ticketsCount > 0){
            ticketsCount--;
            System.out.println(Thread.currentThread().getName()+" 賣了一張票,還剩下:"+ticketsCount);
        }
    }
}
public class TicketsTestRunnable {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        Thread th1 = new Thread(mr,"窗口1");
        Thread th2 = new Thread(mr,"窗口2");
        Thread th3 = new Thread(mr,"窗口3");
        
        th1.start();
        th2.start();
        th3.start();
    }
}

  執行結果:

窗口1 賣了一張票,還剩下:4
窗口3 賣了一張票,還剩下:2
窗口3 賣了一張票,還剩下:1
窗口3 賣了一張票,還剩下:0
窗口2 賣了一張票,還剩下:3

  這是因為創建Thread實例時,使用的是同一個MyRunnable類對象,所以會共享其中的數據。

用戶線程與守護線程

  在Java線程中,共有兩類線程:

  1 用戶線程:用戶代碼生成

  2 守護線程:用於特定的功能,當用戶線程都結束時,守護線程會隨着JVM的停止而停止,因此守護線程不能用於IO操作。

  那么下面一個簡單的守護線程的例子:

  創建一個守護線程,持續不斷的向文件中寫入數據。主線程中啟動該線程,然后主線程在一定時間后,退出。

  觀察守護線程的狀態!

  代碼如下:

package com.imooc.test;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Scanner;

class DaemonThread implements Runnable{
    public void run() {
        System.out.println("進入守護線程:"+Thread.currentThread().getName());
        try {
            Write2File();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("退出守護線程:"+Thread.currentThread().getName());
    }
    public void Write2File() throws Exception{
        File filename = new File("d:"+File.separator+"daemon.txt");
        OutputStream os = new FileOutputStream(filename,true);
        int count = 0;
        while(count < 999){
            os.write(("\r\nword "+count).getBytes());
            System.out.println("守護線程"+Thread.currentThread().getName()+
                    "寫入了 "+count);
            count++;
            Thread.sleep(1000);
        }
    }
}
public class DaemonTest {
    public static void main(String[] args) {
        System.out.println("進入主線程"+Thread.currentThread().getName());
        DaemonThread dt = new DaemonThread();
        Thread th = new Thread(dt);
        th.setDaemon(true);
        th.start();
        
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } 
        
        System.out.println("退出主線程"+Thread.currentThread().getName());
    }
}

  當主線程睡眠了5秒后,便結束。此時JVM中沒有其他的用戶線程,於是守護線程也直接退出。

  執行結果如下:

進入主線程main
進入守護線程:Thread-0
守護線程Thread-0寫入了 0
守護線程Thread-0寫入了 1
守護線程Thread-0寫入了 2
守護線程Thread-0寫入了 3
守護線程Thread-0寫入了 4
退出主線程main

  可以看到守護線程直接就中斷退出了!

  鑒於守護線程的這種特性,常用於實時監控系統狀態。比如數據庫,JVM等等。

查看線程快照

  通過使用Jstack.exe程序,可以幫助用戶查看線程狀態。

  使用方法:

  1 查詢線程PID

  2 在cmd中輸入jstack -l pid

C:\Users\Administrator>jstack -l 5028
2015-04-01 17:43:30
Full thread dump Java HotSpot(TM) Client VM (24.60-b09 mixed mode, sharing):

"Thread-0" daemon prio=6 tid=0x00928800 nid=0x2798 waiting on condition [0x03d4f
000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.imooc.test.DaemonThread.Write2File(DaemonTest.java:27)
        at com.imooc.test.DaemonThread.run(DaemonTest.java:12)
        at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
        - None

"Service Thread" daemon prio=6 tid=0x008de000 nid=0xd64 runnable [0x00000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"C1 CompilerThread0" daemon prio=10 tid=0x008dc400 nid=0x2158 waiting on conditi
on [0x00000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Attach Listener" daemon prio=10 tid=0x008f4800 nid=0x13e0 waiting on condition
[0x00000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Signal Dispatcher" daemon prio=10 tid=0x00905800 nid=0x1c7c runnable [0x0000000
0]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
        - None

"Finalizer" daemon prio=8 tid=0x00875800 nid=0x2460 in Object.wait() [0x03e0f000
]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x23800fc8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        - locked <0x23800fc8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(Unknown Source)
        at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

   Locked ownable synchronizers:
        - None

"Reference Handler" daemon prio=10 tid=0x00870800 nid=0x21c8 in Object.wait() [0
x03b6f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x23800db0> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:503)
        at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)
        - locked <0x23800db0> (a java.lang.ref.Reference$Lock)

   Locked ownable synchronizers:
        - None

"main" prio=6 tid=0x0099c000 nid=0x14b8 waiting on condition [0x0052f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at com.imooc.test.DaemonTest.main(DaemonTest.java:40)

   Locked ownable synchronizers:
        - None

"VM Thread" prio=10 tid=0x0086f000 nid=0x22dc runnable

"VM Periodic Task Thread" prio=10 tid=0x00927000 nid=0x131c waiting on condition


JNI global references: 111


C:\Users\Administrator>

  其中詳細的描述了線程的名字,是否為守護線程,以及狀態等等。

參考

  【1】慕課網Thread VS Runnable:http://www.imooc.com/learn/312


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM