Java中的多线程--一些概念

这一块内容主要参考<Java核心技术> 第10版 .

进程与线程

我们用电脑时打开任务管理器,切换到进程选项卡,里面看到的各种xxx.exe,每一个都是一个进程,而多个线程则构成了一个进程.在这篇博客之前的代码中,都是单线程的--一个main()线程,在抛出异常的时候能看到Exception in thread "main"...类似字样.

线程状态

新建状态

执行new Thread()后,调用start()之前时,这个线程就是新建状态.

public class NewThread {
    public static void main(String[] args) {
        Test t1 = new Test();
        //t1.start();
    }
}

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

就绪状态

对一个线程调用start()之后,就进入了就绪状态,此时线程要做的事情就是等待CPU调度它:获取时间片之后便可以运行.

    t1.start();

 下面是start()的代码:

    public synchronized void start() { 
        if (threadStatus != 0) //线程状态不是"0",即'not yet started'(源码对该变量的解释).
            throw new IllegalThreadStateException(); //抛出非法线程状态异常.
        boolean started = false; //已开始设置为false.
        try {
            start0(); //私有本地方法start0().
            started = true; //已开始设置为true.
        } finally {
            try {
                if (!started) { //开启失败
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            
            }
        }
    }

    private native void start0();

调用之后start()结束. 

下面展示了一种会抛出IllegalThreadStateException的情况,即对一个线程调用两次start():

    public static void main(String[] args) {
        Task task = new Task();
        task.start();
        task.start();
    }

运行结果:
    这是前台线程.
    Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:708)
	at thread.Illegal.main(Illegal.java:7)

运行状态

此时该线程得到了时间片,run()开始执行,但是需要注意的是,线程不是持续运行的,只有获得时间片才能运行,也就是说CPU如果后续没有给它时间片,线程就会进入阻塞状态.宏观上是持续运行,微观上是断续运行的.

下面是Thread中的run():

    public void run() {
        if (target != null) {
            target.run();
        }
    }

target是一个Runnable对象,只有一个run(),一个线程可以继承Thread,也可以实现Runnable接口:

public interface Runnable {
    public abstract void run();
}

值得注意的是,run()不能抛出异常,所以重写run()时,假如需要处理异常,只能使用try/catch/finally. 

阻塞状态

阻塞状态分为多种情况.

1.等待阻塞

调用对象的wait(),此线程会被放入等待池.

2.同步阻塞

即synchronized关键字.如果想要获得此同步锁对象,但是这个锁仍被其他线程占用时,该线程就会进入锁池.

3.其他阻塞

当遇到Thread.sleep(),或有其他线程的插入即join(),此线程放弃时间片段,进入阻塞状态.

死亡状态

线程run()执行完毕,这个线程就进入了死亡状态.

线程属性

线程优先级

我们在任务管理器,右键点击一个进程,选择设置优先级,可以看到六个选项:

 在Java中线程有10个优先级,我们也可以设置优先级:

        t1.setPriority(int newPriority);

范围是1~10,有三个常量:

    public final static int MIN_PRIORITY = 1;  //最低

    public final static int NORM_PRIORITY = 5; //标准

    public final static int MAX_PRIORITY = 10; //最高

越低表示被调度到的可能性越低,最高不代表一定会被调度到,只是可能性最高:

public class NewThread {
    public static void main(String[] args) {
        //创建1-10十个线程.
        Thread t1 = new Task(1); 
        ......
        Thread t10 = new Task(10);
        //将线程优先级分别设置为1-10.
        t1.setPriority(Thread.MIN_PRIORITY); //1
        ......
        t5.setPriority(Thread.NORM_PRIORITY); //5
        ......
        t10.setPriority(Thread.MAX_PRIORITY); //10    
        //启动
        t1.start();......t10.start();
    }
}

class Task extends Thread{
    private int pro;
    Task(int pro) {
        this.pro = pro;
    }
    @Override
    public void run() {
        System.out.println("该线程的优先级为" + pro + ".");
    }
}

其中一次运行结果: 

该线程的优先级为1.
该线程的优先级为6.
该线程的优先级为2.
该线程的优先级为8.
该线程的优先级为5.
该线程的优先级为9.
该线程的优先级为3.
该线程的优先级为10.
该线程的优先级为4.
该线程的优先级为7.

可以看到运行顺序不是按照优先级高低的,再来看看下面的情况:

    public static void main(String[] args) {
        Thread t1 = new Task(1);
        ......
        Thread t7 = new Task(1);
        Thread t8 = new Task(5);
        ......
        Thread t13 = new Task(5);
        Thread t14 = new Task(10);
        ......
        Thread t20 = new Task(10);
        t1.setPriority(Thread.MIN_PRIORITY);
        ......
        t7.setPriority(Thread.MIN_PRIORITY);
        t8.setPriority(Thread.NORM_PRIORITY);
        ......
        t13.setPriority(Thread.NORM_PRIORITY);
        t14.setPriority(Thread.MAX_PRIORITY);
        ......
        t20.setPriority(Thread.MAX_PRIORITY);
        t1.start();......t20.start();
    }

其中一次运行结果: 

该线程的优先级为1.
该线程的优先级为5.
该线程的优先级为5.
该线程的优先级为10.
该线程的优先级为10.
该线程的优先级为10.
该线程的优先级为1.
该线程的优先级为5.
该线程的优先级为1.
该线程的优先级为1.
该线程的优先级为10.
该线程的优先级为10.
该线程的优先级为10.
该线程的优先级为10.
该线程的优先级为5.
该线程的优先级为5.
该线程的优先级为1.
该线程的优先级为5.
该线程的优先级为1.
该线程的优先级为1.

所以设置高优先级只能是被运行的概率高,所以在不必需的时候就不要设置优先级. 

守护线程

顾名思义,守护其它线程,主要是提供一些辅助功能,比如计时.当虚拟机中只剩守护线程时,虚拟机就会退出:

守护线程应该永远不去访问固有资源,如文件,数据库,因为它会在任何时候甚至在一个操作的中间发生中断.

将一个线程设置为守护线程的方法是对它调用setDaemon(),并传入true作为参数:

public class NewThread {
    public static void main(String[] args) throws InterruptedException{
        Daemon daemon = new Daemon();
        Task task = new Task();
        daemon.setDaemon(true);//设置为守护线程.
        daemon.start();        //启动.
        Thread.sleep(5000);   //5秒之后启动Task线程.
        task.start();
    }
}

class Daemon extends Thread {
    static int seconds = 1;
    @Override
    public void run() {
        for (int i = 0; i < 15; i++) { //正常来说该线程应该在15秒后结束
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("守护线程已经运行" + seconds + "秒.");
            seconds ++;
        }
    }
}

class Task extends Thread {
    @Override
    public void run() {
        System.out.println("这是前台线程.");
    }
}

在不对Daemon对象调用setDaemon(true)时,这个程序会从1秒一直运行到15秒,而调用了之后,在前台线程启动并运行完毕之后(5秒多一点点),该线程就会被终止:

守护线程已经运行1秒.
守护线程已经运行2秒.
守护线程已经运行3秒.
守护线程已经运行4秒.
守护线程已经运行5秒.
这是前台线程.

Process finished with exit code 0

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值