线程基础(一)线程中的方法

Thread类中的实例方法

从Thread类中的实例方法和类方法的角度讲解Thread中的方法,这种区分的角度也有助于理解多线程中的方法。实例方法,只和实例线程(也就是new出来的线程)本身挂钩,和当前运行的是哪个线程无关

1、start()

start()方法的作用讲得直白点就是通知"线程规划器",此线程可以运行了,正在等待CPU调用线程对象得run()方法,产生一个异步执行的效果

public class MyThread02 extends Thread
{
    public void run()
    {
        try
        {
            for (int i = 0; i < 3; i++)
            {
                Thread.sleep((int)(Math.random() * 1000));
                System.out.println("run = " + Thread.currentThread().getName());
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
public static void main(String[] args)
{
    MyThread02 mt = new MyThread02();
    mt.start();
       
    try
    {
        for (int i = 0; i < 3; i++)
        {
            Thread.sleep((int)(Math.random() * 1000));
            System.out.println("run = " + Thread.currentThread().getName());
        }
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}
run = Thread-0
run = main
run = main
run = main
run = Thread-0
run = Thread-0

结果表明了:CPU执行哪个线程的代码具有不确定性

public class MyThread03 extends Thread
{
    public void run()
    {
        System.out.println(Thread.currentThread().getName());
    }
}
public static void main(String[] args)
{
    MyThread03 mt0 = new MyThread03();
    MyThread03 mt1 = new MyThread03();
    MyThread03 mt2 = new MyThread03();
        
    mt0.start();
    mt1.start();
    mt2.start();
}
Thread-1
Thread-2
Thread-0

尽管启动线程是按照mt0、mt1、mt2,但是实际的启动顺序却是Thread-1、Thread-2、Thread-0。这个例子说明了:调用start()方法的顺序不代表线程启动的顺序,线程启动顺序具有不确定性


2、run()

线程开始执行,虚拟机调用的是线程run()方法中的内容 稍微改一下之前的例子看一下

public static void main(String[] args)
{
    MyThread02 mt = new MyThread02();
    mt.run();
        
    try
    {
        for (int i = 0; i < 3; i++)
        {
            Thread.sleep((int)(Math.random() * 1000));
            System.out.println("run = " + Thread.currentThread().getName());
        }
    }
    catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}
run = main
run = main
run = main
run = main
run = main
run = main

说明如果只有run()没有start(),Thread实例run()方法里面的内容是没有任何异步效果的,全部被main函数执行。换句话说,只有run()而不调用start()启动线程是没有任何意义的。


3、isAlive()

测试线程是否处于活动状态,只要线程启动且没有终止,方法返回的就是true

/**
 * isAlive()方法
 */
public class Thread_isAlive {
	
	static class MyThread extends Thread{
		@Override
		public void run() {
			System.out.println("run:"+Thread.currentThread().isAlive());
		}
	}
	
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		System.out.println("begin:"+myThread.isAlive());
		myThread.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("end:"+myThread.isAlive());
	}
begin:false
run:true
end:false

main函数中加上Thread.sleep(1000)的原因是为了确保Thread的run()方法中的代码执行完,否则有可能end这里打印出来的是true


4、isDaeMon、setDaemon(boolean on)

讲解两个方法前,首先要知道理解一个概念。Java中有两种线程,一种是用户线程,一种是守护线程。守护线程是一种特殊的线程,它的作用是为其他线程的运行提供便利的服务,最典型的应用便是GC线程。如果进程中不存在非守护线程了,那么守护线程自动销毁,因为没有存在的必要,为别人服务,结果服务的对象都没了,当然就销毁了。

理解了这个概念后,看一下例子

public class MyThread11 extends Thread
{
    private int i = 0;
    
    public void run()
    {
        try
        {
            while (true)
            {
                i++;
                System.out.println("i = " + i);
                Thread.sleep(1000);
            }
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
public static void main(String[] args)
    {
        try
        {
            MyThread11 mt = new MyThread11();
            mt.setDaemon(true);
            mt.start();
            Thread.sleep(5000);
            System.out.println("我离开thread对象再也不打印了,我停止了!");
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

看一下运行结果:

 1 i = 1
 2 i = 2
 3 i = 3
 4 i = 4
 5 i = 5
 6 我离开thread对象再也不打印了,我停止了!

要解释一下。我们将MyThread11线程设置为守护线程,看到第6行的那句话,而i停在6不会再运行了。这说明,main线程运行了5秒多结束,而i每隔1秒累加一次,5秒后main线程执行完结束了,MyThread11作为守护线程,main函数都运行完了,自然也没有存在的必要了,就自动销毁了,因此也就没有再往下打印数字。

关于守护线程,有一个细节注意下,setDaemon(true)必须在线程start()之前


5、getPriority()  setPriority(int newPriority)

java线程优先级

JAVA线程的优先级取值范围是1 (Thread.MIN_PRIORITY ) 到 10 (Thread.MAX_PRIORITY )。如果没有设置, 线程默认的优先级是NORM_PRIORITY。这个值是5。

优先级高的CPU得到的CPU资源比较多,设置优先级有助于帮"线程规划器"确定下一次选择哪一个线程优先执行。换句话说,两个在等待CPU的线程,优先级高的线程越容易被CU选择执行

/**
 * 线程优先级
 */
public class Thread_priority {
	static class MyThread01 extends Thread{
		@Override
		public void run() {
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName()+"优先级:"+Thread.currentThread().getPriority());
			}
		}
	}
	
	static class MyThread02 extends Thread{
		@Override
		public void run() {
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName()+"优先级:"+Thread.currentThread().getPriority());
			}
		}
	}
	
	public static void main(String[] args) {
			MyThread01 t1 = new MyThread01();
			t1.setPriority(9);
			t1.start();
			MyThread02 t2 = new MyThread02();
			t2.start();
	}
}
Thread-0优先级:9
Thread-0优先级:9
Thread-0优先级:9
Thread-0优先级:9
Thread-0优先级:9
Thread-1优先级:5
Thread-1优先级:5
Thread-1优先级:5
Thread-1优先级:5
Thread-1优先级:5

线程的优先级是有一定的规则性的,CPU会尽量将执行资源让给优先级比较高的线程

上面说到的第二点就是验证线程的规则性,在优先级越相近的情况,线程执行的顺序是随机的,CPU会尽量将执行资源让给优先级比较高的线程这一说法是对的,但是实际运行的情况是多种多样的,所以线程充满了随机性



Thread类中的静态方法

1、currentThread()

currentThread()方法返回的是对当前正在执行线程对象的引用

/**
 * currentThread()
 */
public class Thread_currentThread extends Thread{
	static{
		System.out.println(Thread.currentThread().getName()+"--执行静态块");
	}
	
	public Thread_currentThread(){
		System.out.println(Thread.currentThread().getName()+"--执行构造方法");
	}
	
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"--执行run方法");
	}
	
	public static void main(String[] args) {
		Thread_currentThread t1= new Thread_currentThread();
		t1.start();
	}
}
main--执行静态块
main--执行构造方法
Thread-0--执行run方法

线程类的构造方法、静态块是被main线程调用的,而线程类的run()方法才是应用线程自己调用的


2、sleep(long millis)

sleep(long millis)方法的作用是在指定的毫秒内让当前"正在执行的线程"休眠(暂停执行)。这个"正在执行的线程"是关键,指的是Thread.currentThread()返回的线程。根据JDK API的说法,"该线程不丢失任何监视器的所属权",直白点讲,就是不让出CPU资源。CPU还在执行当前线程run()方法中的代码,sleep不会放开锁,无非执行的内容是"睡觉"而已。

/**
 * sleep()休眠
 */
public class Thread_sleep extends Thread{
	@Override
	public void run() {
			System.out.println(this.getName()+"--begin");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(this.getName()+"--end");
	}
	
	public static void main(String[] args) {
		Thread_sleep t = new Thread_sleep();
		t.start();
	}
}
Thread-0--begin
Thread-0--end

begin后停顿2秒执行end


3、yield()

暂停当前执行的线程对象,并执行其他线程。这个暂停是会放弃CPU资源的,并且放弃CPU的时间不确定,有可能刚放弃,就获得CPU资源了,也有可能放弃好一会儿,才会被CPU执行。yield也不会放开锁。

public class MyThread08 extends Thread
{
    public void run()
    {
        long beginTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < 50000000; i++)
        {
            Thread.yield();
            count = count + i + 1;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用时:" + (endTime - beginTime) + "毫秒!");
    }
}
public static void main(String[] args)
{
    MyThread08 mt = new MyThread08();
    mt.start();
}
用时:3264毫秒!
用时:3299毫秒!
用时:3232毫秒!
用时:3256毫秒!
用时:3283毫秒!
用时:3504毫秒!
用时:3378毫秒

看到,每次执行的用时都不一样,证明了yield()方法放弃CPU的时间并不确定。


4、interrupted()

测试当前线程是否已经中断,执行后具有将状态标识清除为false的功能。换句话说,如果连续两次调用该方法,那么返回的必定是false:

public class Thread_interrupted {
	public static void main(String[] args) {
		Thread.currentThread().interrupt();
		System.out.println(Thread.currentThread().getName()+"是否中断:"+Thread.interrupted());
		System.out.println(Thread.currentThread().getName()+"是否中断:"+Thread.interrupted());
		System.out.println(Thread.currentThread().getName()+"是否中断:"+Thread.interrupted());
	}
}
main是否中断:true
main是否中断:false
main是否中断:false

猜你喜欢

转载自blog.csdn.net/baiyan3212/article/details/86488603
今日推荐