多线程<基础>

线程的生命周期: 

当线程被创建并启动后,它并不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期中,它要经过新建(New),就绪(Runnable),运行(Running),阻塞(Blocked)和死亡(Dead) 5种状态。

  1. 新建(New)

    使用new关键字创建一个线程之后,该线程就处于新建状态,此时它和其他Java对象一样,仅仅由Java虚拟机为其分配内存,并初始化了其成员变量。此时的线程对象没有表现出任何现成的动态特征,程序也不会执行线程执行体中的线程执行体。

  2. 就绪(Runnable)

    当线程对象调用了start()方法后,此时该线程处于就绪状态。Java虚拟机为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,它只是表示该线程可以运行了。至于该线程何时开始运行,取决于JVM里线程调度器的调度。

    注意: 

    #1  启动线程使用start方法,而不是run方法!调用start方法来启动线程,系统会把该run方法当成线程执行体来处理。但如果直接调用线程对象的run方法,则run方法立即就会被执行,且在run方法返回之前其他线程无法并发执行----也就是说系统吧线程对象当成普通对象,而run方法仅是一个普通方法,而不是线程执行体。

        #2  不要对已经处于启动状态的线程再次调用start方法,否则将引发IllegalThreadStateException异常。

    3. 运行(Running)

        如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态。如果计算机只有一个CPU,在任何时刻只有一条线程处于运行状态。当然,在一个多处理器的机器上,将会有多个线程并行执行。但当线程数大于处理器数时,依然会有多条线程在同一个CPU上轮换的现象。 

    4. 阻塞(Blocked)

        当发生如下情况,线程将进入阻塞状态:

        a. 线程调用sleep方法主动放弃所占用的处理器资源;

        b. 线程调用了一个阻塞式 IO方法,在该方法返回之前,该线程被阻塞;

        c. 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有;

        d. 线程在等待某个通知(notify);

        e. 程序调用了线程的suspend方法将线程挂起。

        解除阻塞的线程后,该线程重新进入就绪状态,等待线程调度器再次调度它。

    5. 线程死亡(Dead)

        线程会以以下三种方式之一结束,结束后该线程处于死亡状态:

        a. run()方法执行完成,线程正常结束;

        b. 线程抛出一个未捕获的Exception或Error;

        c. 直接调用该线程的stop()方法来结束该线程---易导致死锁,不使用!

        注意:  当主线程结束时,其他线程不受任何影响,并不会随之结束,一旦子线程启动后,它就拥有和主线程相同的地位,它不会受主线程的影响。

        091858_SgqX_2265030.png

        为测试某条线程是否已经死亡,可以调用线程对象的isAlive()方法,当线程处于就绪、运行、阻塞三种状态时,该方法将返回true;当线程处于新建、死亡两种状态时,该方法将返回false。

控制线程:

Java的线程支持提供了一些简便的方法,通过这些方法可以很好地控制线程的执行.

  1. join线程

    Thread提供了一个线程等待另一个线程完成的方法----join()方法. 当在某个执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法计入的join线程执行完为止.

package org.santorini.thread;

public class JoinTest {
    public static void main(String[] args) throws Exception {
        new JoinThread("新线程").start();;
		
        for (int i=0; i<100; i++) {
            if (i == 20) {
                JoinThread jt = new JoinThread("被Join的线程");
		jt.start();
		//main线程调用了jt线程的join()方法,main线程必须等待jt线程执行结束后才会向下执行
		jt.join();
	    }
            System.out.println(Thread.currentThread().getName() + "^^^^^" + i);
        }
    }
}

class JoinThread extends Thread {
        public JoinThread(String name) {
            super(name);
	}
	    
	@Override
	public void run() {
	    for (int i=0; i<50; i++) {
                System.out.println(getName() + "---" + i);
            }
        }
}

    085844_MIYe_2265030.png

        说明: 程序中共有3个线程,main方法中开始就启动了名为"新线程"的子线程,该线程启动后与主线程拥有同等地位,并发执行。当主线程的循环变量 i=20 时,启动了名为"被Join的线程",该线程不会和主线程并发执行,而是主线程必须等该线程执行结束后才可以向下执行,这段时间内主线程处于等待状态。

        join方法有如下3中重载形式:    

    • join(): 等待被join的线程执行完成;

    • join(long millis): 等待被join的线程的时间最长为millis毫秒。如果在millis毫秒内被join的线程还没有之行结束,则不再等待;

    • join(long millis, int nanos): 等待被join的线程的时间最长为millis毫秒加nanos微毫秒。

    2. 后台线程

        有一种线程,它是在后台运行的,它的任务是为其他的线程提供服务,这种线程被称为"后台线程"。又称为"守护线程"或"精灵线程"(PS:好吧,我喜欢叫它"精灵线程",略显调皮~)。JVM的垃圾回收线程就是典型的后台线程。

        后台线程的特征: 如果所有的前台线程都死亡,后台线程会自动死亡.

        调用Thread对象的setDaemon(true)方法可将指定线程设置为精灵线程,

        Thread类还提供了一个isDaemon()方法,用于判断指定线程是否为后台线程。

        注意: 

        a. 前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认是后台线程;

        b. 前台线程死亡后,JVM会通知后台线程死亡,但从它接受指令到做出响应,需要一定时间;

        c. 将某个线程设置为后台线程,必须在该线程启动之前设置,也就是说,setDaemon(true)必须start()方法之前调用,否则会引发IllegalThreadStateException异常.

    3. 线程睡眠:sleep

        如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。

        当当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间段内,该线程不会获得执行的机会,即使系统中没有其他可执行的线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行.

    4. 线程让步: yield

        yield()方法是一个和sleep方法有点类似的方法,它也是一个Thread类提供的静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态。yield只是让当前线程暂停一下,让系统的线程调度器重新调度一次。完全可能的情况是: 当某个线程调用了yield方法暂停之后,线程调度又将其调度出来重新执行. 

    关于sleep方法和yield方法的区别如下:

    A. sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级。但yield方法只会给优先级相同,或优先级更高的线程执行机会;

    B. sleep方法将方法转入阻塞状态,直到经过阻塞事件才会转入就绪状态。而yield不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。

    C. sleep方法声明抛出了InterruptedException异常,所以调用sleep方法时要么捕捉该异常,要么显式声明抛出该异常。而yield方法则没有声明抛出异常。

改变线程优先级:

    每个线程执行时都有一定的优先级,优先级高的线程获得较多的执行机会,而优先级低的线程则获得较少的执行机会。

    每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级,由main线程创建的子线程也有普通优先级。

    Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回执行线程的优先级,其中setPriority方法的参数可以使一个整数,范围是1~10之间,也可以是Thread类的三个静态常量:

    MAX_PRIORITY: 其值是10;

    MIN_PRIORITY: 其值是1;

    NORM_PRIORITY: 其值是5;

转载于:https://my.oschina.net/LvSantorini/blog/484496

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值