多线程基础(2)

本文介绍了多线程中的Sleeping、Priority和Daemon Threads概念。Sleeping通过调用sleep()让任务暂停执行;Priority允许设定线程优先级,但实际效果受CPU调度影响;Daemon Threads是后台服务线程,当所有非守护线程结束时,程序会终止。文章强调了不能依赖yield来控制任务顺序,并提醒读者理解守护线程的特性及其对程序终止的影响。

Sleeping

调用sleep()是一种简单的影响你的任务执行的方式,能让你的任务停止执行一会儿。

例子:

public class SleepingTask extends LiftOff {

    @Override
    public void run() {
        try {
            while (countDown-- > 0) {
                System.out.println(status());
                TimeUnit.MILLISECONDS.sleep(100);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

调用sleep()可能会抛出一个interruptedException,可以在run()方法中被捕获。这个异常不能被传播回主线程。(run方法也没有办法抛出,因为方法的签名上没有抛异常)

Priority

线程的优先级给调度器传递了这个线程的重要性。尽管CPU调度一组线程的顺序是不确定的,但是调度器还是倾向于调度那些优先级比较高的在等待中的线程(很随机,没有用)。

例子:

public class SimplePriority implements Runnable {

    private int countDown = 5;
    private volatile double d;
    private int priority;

    public SimplePriority(int priority) {
        this.priority = priority;
    }

    @Override
    public String toString() {
        return Thread.currentThread() + ":" + countDown;
    }

    @Override
    public void run() {
        Thread.currentThread().setPriority(priority);
        while (true) {
            for (int i = 0; i < 100000; i++) {
                d += (Math.PI + Math.E) / (double) i;
                if (i % 1000 == 0) {
                    Thread.yield();
                }
            }
            System.out.println(this);
            if (--countDown == 0) {
                return;
            }
        }
    }
}

public class MoreBasicThreads {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            executorService.execute(new SimplePriority(Thread.MIN_PRIORITY));
        }
        executorService.execute(new SimplePriority(Thread.MAX_PRIORITY));
        executorService.shutdown();
    }
}

Thread[pool-1-thread-6,10,main]:5
Thread[pool-1-thread-2,1,main]:5
Thread[pool-1-thread-4,1,main]:5
Thread[pool-1-thread-1,1,main]:5
Thread[pool-1-thread-5,1,main]:5
Thread[pool-1-thread-6,10,main]:4
Thread[pool-1-thread-3,1,main]:5
Thread[pool-1-thread-1,1,main]:4
Thread[pool-1-thread-4,1,main]:4
Thread[pool-1-thread-6,10,main]:3
Thread[pool-1-thread-2,1,main]:4
Thread[pool-1-thread-5,1,main]:4
Thread[pool-1-thread-6,10,main]:2

有效果,但是不是特别明显,因为其他的低优先级的线程先结束了。

在run方法里面,100,000个重复的浮点数计算,包括了双精度的加和除。volatile保证了没有编译器的优化?(不是很明白)如果注释掉forloop,那么优先级的效果就大打折扣了。(主要是还是如果没有复杂的运算,那么处理器不会倾向于调度别的任务,可以用yield或者sleep达到同样的效果)。

Yielding

使用yield可以告诉调度器这个任务已经完成的差不多了,可以给另外的任务一些调度的机会。但是这个不是强制的,不保证一定会切换。在LiftOff那个例子中,如果使用yield的话,每一个任务完成的时候大概率是在最后。如果去掉了yield,则会出现某一个任务提前做完的情况(调度器不倾向于切换)。

    @Override
    public void run() {
        while (countDown-- > 0) {
            System.out.println(status());
            Thread.yield();
        }
    }

#0(9),
#1(9),
#2(9),
#3(9),
#4(9),
#1(8),
#0(8),
#2(8),
#3(8),
#4(8),
#1(7),
#0(7),
#2(7),
#3(7),
#4(7),
#1(6),
#0(6),
#2(6),
#3(6),
#4(6),
#1(5),
#0(5),
#2(5),
#3(5),
#4(5),
#1(4),
#0(4),
#2(4),
#3(4),
#4(4),
#1(3),
#0(3),
#2(3),
#3(3),
#4(3),
#1(2),
#0(2),
#4(2),
#3(2),
#2(2),
#1(1),
#0(1),
#4(1),
#3(1),
#2(1),
#1(LiftOff!),
#0(LiftOff!),
#4(LiftOff!),
#3(LiftOff!),
#2(LiftOff!),

    @Override
    public void run() {
        while (countDown-- > 0) {
            System.out.println(status());
            //Thread.yield(); 去掉yield以后
        }
    }

#0(9),
#1(9),
#1(8),
#1(7),
#1(6),
#1(5),
#1(4),
#1(3),
#1(2),
#1(1),
#1(LiftOff!),
#2(9),
#2(8),
#2(7),
#2(6),
#2(5),
#2(4),
#2(3),
#2(2),
#2(1),
#2(LiftOff!),
#0(8),
#0(7),
#0(6),
#0(5),
#0(4),
#0(3),
#0(2),
#0(1),
#0(LiftOff!),
#3(9),
#3(8),
#3(7),
#3(6),
#3(5),
#3(4),
#4(9),
#4(8),
#4(7),
#4(6),
#4(5),
#4(4),
#4(3),
#4(2),
#4(1),
#4(LiftOff!),
#3(3),
#3(2),
#3(1),
#3(LiftOff!),

不能依靠yield来控制任务的完成次序!!!

Daemon Threads (守护线程)

守护线程倾向于提供一个通用的后台服务,但是不是你的程序必要的一部分。当所有的非守护线程完成了,那么这个程序就会终止,杀死所有的守护线程。反过来,如果有非守护线程在run则这个程序不会终止。

例子

public class SimpleDaemons implements Runnable {
    @Override
    public void run() {
        try {
            while (true) {
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println(Thread.currentThread() + " " + this);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("sleep() interrupted");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new SimpleDaemons());
            thread.setDaemon(true);
            thread.start();
        }
        System.out.println("all deomons started");
        TimeUnit.MILLISECONDS.sleep(175);
    }
}

你必须设置thread成为守护线程,在start()之前调用setDaemon()。

需要了解的部分:

1. 从守护线程中出生的线程也是守护线程;

例子:

class Daemon implements Runnable {
    private Thread[] t = new Thread[10];

    @Override
    public void run() {
        for (int i = 0; i < t.length; i++) {
            t[i] = new Thread(new DaemonSpawn());
            t[i].start();
            System.out.println("daemon" + i + "started");
        }

        for (int i = 0; i < t.length; i++) {
            System.out.println("i is deamon" + t[i].isDaemon());
        }

        while (true){
            Thread.yield();
        }
    }
}

class DaemonSpawn implements Runnable {

    @Override
    public void run() {
        while (true) {
            Thread.yield();
        }
    }
}

public class Daemons{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Daemon());
        thread.setDaemon(true);
        thread.start();
        System.out.println("thread is deamon:" + thread.isDaemon());
        TimeUnit.SECONDS.sleep(1);
    }

}

2. 守护线程不会执行finally 

public class DaemonsDontRunFinally {
    public static void main(String[] args) {
        Thread thread = new Thread(new ADaemon());
        thread.setDaemon(true);
        thread.start();
    }
}

class ADaemon implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println("running");
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally");
        }
    }
}

关于线程池:

尽量使用ThreadPoolExecutor,使用Exectors.newCachedThreadPool的内部实现也是使用的ThreadPoolExecutor。还是需要仔细了解!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值