Java之Thread浅谈

进程与线程

什么是进程?进程是操作系统结构的基础,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的基本单位。进程可以被看作程序的实体,同样,它也是线程的容器。

什么是线程?线程是操作系统调度的最小单元,也叫作轻量级进程。在一个进程中可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。

线程的状态

  • New:新创建的状态。线程被创建,还没有调用start方法,在线程运行之前还有一些基础工作要做。
  • Runnable:可运行状态。一旦调用start方法,线程就处于Runnable状态。一个可运行的线程可能正在运行也肯能没有运行,这取决于操作系统给线程提供运行的时间。
  • Blocked:阻塞状态。表示线程被锁阻塞,它暂时不活动。
  • Waiting:等待状态。线程暂时不活动,并且不运行任何代码,这消耗最少资源,直到线程调度器重新激活它。
  • Timed waiting:超时等待状态。和等待状态不同的是,它是可以在指定的时间自行返回的。
  • Terminated:终止状态。表示当前线程已经执行完毕。导致线程终止有两种情况:第一种就是run方法执行完毕正常退出;第二种就是因为一个没有捕获的异常而终止了run方法,导致线程进入终止状态。

线程的简单应用

上面已经说过,线程有多种状态,那么为什么需要这几种状态?或者说哪些情况会进入到这些状态?下面我们举例说明。

  • New新创建状态
Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {         
            //do something...   
    }
});
System.out.println(thread.getState());

我们看打印的结果:Thread-0:NEW,可以证明该线程处于New状态。

  • Runnable可运行状态
Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {         
            
    }
});
thread.start();
System.out.println(thread.getState());

首先看下打印的结果:Thread-0:RUNNABLE。线程创建完成后我们调用start方法,线程会进入Runnable状态。

  • Blocked阻塞状态

这里解释下什么叫阻塞状态?运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,该线程将进入阻塞状态,如下示例。

public class Thread9 {

    static Object object = new Object();
    
    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();
        ThreadB threadB = new ThreadB();
        threadA.setName("threadA");
        threadB.setName("threadB");
        threadA.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        threadB.start();
        System.out.println(threadA.getName() + ":" + threadA.getState());
        System.out.println(threadB.getName() + ":" + threadB.getState());

    }
    
    static class ThreadA extends Thread{
        
        @Override
        public void run() {
            synchronized (object) {
                while(true) {
                }
            }
        };
    }
    
   static class ThreadB extends Thread{
        
        @Override
        public void run() {
            synchronized (object) {
                System.out.println("ThreadB");
            }
        };
    }

}

打印结果:

threadA:RUNNABLE
threadB:BLOCKED

上面的例子中,开启两个线程ABA、B线程的run方法中获取同一对象的同步锁,由于A线程一直持有Object对象,B线程的任务无法进行,会进入阻塞状态。当A线程任务完成或出现异常将不再持有该对象时,B线程将进入到Runnable状态。

  • Waiting等待状态

线程暂时不活动,并且不运行任何代码,调用wait()join()(实际上也是调用wait()方法)方法可进入该状态,调用notify()notifyAll()方法解除状态,示例如下。

调用join()方法

package thread;

public class JoinTest {

    public static void main(String[] args) throws InterruptedException{
        Thread1 thread1 = new Thread1();
        thread1.setName("Thread-1");
        thread1.start();
        System.out.println("begin");
        Thread.sleep(2000);
        System.out.println(thread1.getName() + ":" + thread1.getState());
    }
    
    static class Thread1 extends Thread {
        
        @Override
        public void run(){
            
            Thread thread2 = new Thread(new Runnable() {
                
                @Override
                public void run() {
                    
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("Thread-2:running");
                    } 
                }
            });
            thread2.setName("Thread-2");
            thread2.start();
            try {
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end");
        }
    }
    
}

打印结果:

begin
Thread-2:running
Thread-1:WAITING
Thread-2:running
Thread-2:running
Thread-2:running
Thread-2:running
end

在线程1中再开启一个线程2,线程2开始后调用join()方法,然后查看线程1的状态。

调用wait()方法

package thread;

import java.util.concurrent.TimeUnit;

public class WaitTest{
    

    public synchronized void waittest() throws InterruptedException {
        System.out.println("start");
        wait();
        System.out.println("over");
    }
    
    public synchronized void notifyTest() {
        notifyAll();
    }
    
    public static void main(String[] args) throws InterruptedException{
        WaitTest waitTest = new WaitTest();
        Thread mainThread = Thread.currentThread();
        MyThread myThread = new MyThread(mainThread,waitTest);
        mainThread.setName("Thread-main");
        myThread.setName("Thread-0");
        myThread.start();
        waitTest.waittest();
        
    }
    
    static class MyThread extends Thread{
        
        Thread thread;
        WaitTest waitTest;
        
        public MyThread(Thread thread,WaitTest waitTest) {
            this.thread = thread;
            this.waitTest = waitTest;
        }
        
        @Override
        public void run() {
            
            try {
                Thread.sleep(200);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            
            System.out.println(thread.getName() 
                    + ":" + thread.getState());
            
            int i = 0;
            do {
                i++;
                try { 
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(this.currentThread().getName() 
                        + ":" + this.currentThread().getState());
                
            } while (i < 5);
            
            waitTest.notifyTest();
        }
        
    }

}

打印结果:

start
Thread-main:WAITING
Thread-0:RUNNABLE
Thread-0:RUNNABLE
Thread-0:RUNNABLE
Thread-0:RUNNABLE
Thread-0:RUNNABLE
over

开启一个新线程,然后主线程调用wait()方法(必须使用同步字synchronized加锁,否则会报异常),查看主线程的状态。

通过上面两个示例我们可以看到,调用wait()join()方法都可以使线程进入WAITING状态,但是它们两者是有区别的(它们的区别以后会讲到,这里就不详细比较了)。

  • Timed waiting超时等待状态

Timed waitingWaiting的不同之处在于,Timed waiting有时间限制,时间到了之后将自行解除该状态。

package thread;

public class TimeWaitIngTest {

    public static void main(String[] args) {
        
        ThreadA threadA = new ThreadA();
        ThreadA threadB = new ThreadA();
        ThreadA threadC = new ThreadA();
        threadA.setName("thread-A");
        threadB.setName("thread-B");
        threadC.setName("thread-C");
        threadA.start();
        threadB.start();
        threadC.start();
        
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println(threadA.getName() 
                + ":" + threadA.getState());
        
        System.out.println(threadB.getName() 
                + ":" + threadB.getState());
        
        System.out.println(threadC.getName() 
                + ":" + threadC.getState());

        
    }
    
    static class ThreadA extends Thread{
        
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
   static class ThreadB extends Thread{
        
        @Override
        public void run() {
            try {
                wait(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
   
   static class ThreadC extends Thread{
        
        @Override
        public void run() {
            try {
                join(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

打印结果:

thread-A:TIMED_WAITING
thread-B:TIMED_WAITING
thread-C:TIMED_WAITING

开启三个线程,分别调用sleep(long)wait(long)join(long)方法,看这三个线程的状态。

  • TERMINATED终止状态
package thread;

public class TerminatedTest {

    public static void main(String[] args) throws InterruptedException{
        
        MyThread myThread = new MyThread();
        myThread.setName("MyThread");
        System.out.println(myThread.getName() 
                + ":" + myThread.getState());
        myThread.start();
        Thread.sleep(10);
        System.out.println(myThread.getName() 
                + ":" + myThread.getState());

    }
    
    static class MyThread extends Thread{
        
        @Override
        public void run() {
            
        }
    }

}

打印结果:

MyThread:NEW
MyThread:TERMINATED

新建一个线程,然后执行,看线程的状态变化。

综上所诉,线程一般有6种状态,下面用一张图来总结


线程状态
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容