Java Runnable and Thread

来源:互联网 发布:telnet 端口号不通 编辑:程序博客网 时间:2024/05/17 09:47

I. 简介

线程机制是开发中非常重要、也是非常复杂的一个环节. 那么为什么需要线程?
首先,在程序的执行中,不可避免地会遇到一些需要等待的任务,比如从数据库请求数据、做一些耗时操作等. 而Java 语言本身的任务处理机制是顺序控制流,也就是说,把所有任务排成一个队列,只有第一个任务执行完毕,第二个任务才能执行,第三个任务则要等第二个任务完成…
如果没有多线程的加入,那用户在使用程序时,就只能在某些时候等待一些操作完成,这势必会影响用户体验. 因此,线程的加入,大大地提升了程序的实用性,但同时也增加了程序设计的难度.

II. 线程实质

关于线程需要注意的一点是,即使是在多核处理器上运行,线程机制其实是在单个处理器上交替着完成不同的任务. 比如现在有Task A, Task B 和Task C;这三个任务现在用Java 的多线程机制来解决,情况将会是这样:CPU 给Task A分配了一个小时间块来执行Task A,如果Task A没有完成,那么CPU 现在也会转移到处理Task B上,并给Task B分配同样长度的时间块,并定时切换到处理Task C 去,然后再分给Task C 的时间结束后,再次返回处理Task A,如此循环,直到处理完所有的任务. 但因为CPU 自身的速度够快,会给人一种多个任务在同时执行的错觉.

III. Runnable

介绍完了线程内部的运作方法,下面首先会讲解Runnable 接口.
Runnable 接口自身是不参与线程分配和调度的,它自身的存在目的其实是规定某个线程内的任务要如何执行.

public class Runnables implements Runnable {    private int countdown = 5;    private int id;    public Runnables(int id) {        this.id = id;    }    public String status() {        return id + " (" + (countdown > 0 ? countdown: "finished")                         + "), ";    }    @Override    public void run() {        while (--countdown > 0) {            System.out.print(status());        }    }    public static void main(String [] args) {        for (int i = 0; i < 5; i++) {            Runnables runnables = new Runnables(i);            runnables.run();            System.out.println();        }    }}

在这个例子中,Runnables 执行了Runnable 接口,并重写了run()方法;某个task 需要执行的操作,便是写在run()的.
但由于Runnable 接口本身不具备分发和调用线程的能力,所以所使用的都是同一个线程,即系统分发给main()函数的线程,并且每个任务都是等上一个任务执行完后再执行的.
而这一点也可以从返回值中看出.

output://0(4), 0(3), 0(2), 0(1), 1(4), 1(3), 1(2), 1(1), 2(4), 2(3), 2(2), 2(1), 3(4), 3(3), 3(2), 3(1), 4(4), 4(3), 4(2), 4(1), 

IV. Thread

如果说Runnable 接口是用来规定一个线程中的任务,那么Thread 类便是分发、调度线程的工具了.
在下面的示例中,使用了Thread 来执行 Runnables 类.

    public static void main(String [] args) {        for (int i = 0; i < 5; i++) {            Thread thread = new Thread(new Runnables(i));            thread.start();            System.out.println();        }    }

在新建了一个Thread 类对象后,调用start()方法开始线程. 而每一个线程将会在什么时候运行是无法知道的. 打印台的输出便很好的说明了这一点.

output://0(4), 0(3), 0(2), 0(1), 1(4), 1(3), 1(2), 1(1), 2(4), 2(3), 2(2), 2(1), 3(4), 3(3), 3(2), 3(1), 4(4), 4(3), 4(2), 4(1), 

Executor

Executor 能代替开发者管理Thread 对象,从而简化开发过程. 它是客户端与任务执行之间的一个简介层.

    public static void main(String [] args) {        ExecutorService ex = Executors.newCachedThreadPool();        for (int i = 0; i < 5; i++)             ex.execute(new Runnables(i));        ex.shutdown();    }

通过创建Executor 的对象,并调用execute() 方法,就可以启动一个线程了. 而shutdown()在调用后,将不可通过ex这个对象创建新的线程了.
同时,这里获得ExecutorService 对象的,是Executors 的方法. 通常会使用newCachedThreadPool(),但如果需要规定线程数量,可调用newFixedThreadPool()并传入参数指定线程数,或者调用newSingleThreadExecutor() 返回只能启动一条线程的Executor.

V. Callable

如果需要任务在完成后,返回一个值,就需要使用Callable 而不是Runnable 接口了. Callable 接口需要执行call()方法,并返回一个值.

public class Callabel implements Callable<String> {    private static int count = 0;    private int id = ++count;    public Callabel() {}    @Override    public String call() throws Exception {        return Integer.toString(id);    }    public static void main(String [] args) {        ExecutorService ex = Executors.newCachedThreadPool();        ArrayList<Future<String>> resutls = new ArrayList<>();        for (int i = 0; i < 5; i++) {            resutls.add(ex.submit(new Callabel()));        }        for (Future<String> result: resutls) {            try {                // get() blocks until completion                System.out.println(result.get());            } catch (InterruptedException e) {                e.printStackTrace();            } catch (ExecutionException e) {                e.printStackTrace();            }            finally {                ex.shutdown();            }        }    }}

这里返回了一个String 对象,并通过类型为Future< String >的数组把结果保存下来了. 也就是说,如果想要获得Callable 返回的数值,需要一个Future 对象来保存,并通过get()方法来获取值.

VI. Thread 其他方法

TimeUnit sleep() 休眠

使用TimeUnit 的sleep() 方法可以让线程休眠,时间取决于参入的参数.

public class Sleep implements Runnable{    private static int count = 0;    private final int id = ++count;    @Override    public void run() {        try {            System.out.print(id + "a  ");            TimeUnit.SECONDS.sleep(2);            System.out.print(id + "b  ");            System.out.println();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void main(String [] args) {        ExecutorService executor = Executors.newCachedThreadPool();        for (int i = 0; i < 5; i++) {            executor.execute(new Sleep());        }    }}

此处,每个线程会休眠2秒.

Thread.yield() 让步

让步的意思是说:我的任务执行完了,现在我将CPU让步给其他线程.

public class Yield implements Runnable{    private static int count = 0;    private final int id = ++count;    @Override    public void run() {        int sum = 0;        for (int i = 0; i < 10; i++) {            sum += i;            if (sum > 10) {                System.out.println("id:" + id + " ==> " + sum);                Thread.yield();            }        }    }    public static void main(String [] args) {        ExecutorService executor = Executors.newCachedThreadPool();        for (int i = 0; i < 5; i++) {            executor.execute(new Yield());        }    }}output://id:1 ==> 15id:3 ==> 15id:2 ==> 15id:4 ==> 15id:5 ==> 15id:4 ==> 21id:3 ==> 21id:1 ==> 21id:4 ==> 28id:5 ==> 21id:2 ==> 21id:5 ==> 28id:2 ==> 28id:5 ==> 36id:4 ==> 36id:1 ==> 28id:3 ==> 28id:4 ==> 45id:5 ==> 45id:2 ==> 36id:3 ==> 36id:1 ==> 36id:3 ==> 45id:1 ==> 45id:2 ==> 45

每个线程在启动后,开始执行加法运算. 当sum大于10时,线程会暂停当前任务,将CPU 让给下一个线程. 所以从前5个输出中可以看到,线程都是在sum 到达15后暂停的进程.

Thread setPriority() 优先级

尽管各个线程的执行是随机的,但CPU 总会被优先分配给优先级最高的线程执行任务.

public class Priority implements Runnable {    private static int count = 0;    private final int id = ++count;    @Override    public void run() {        int sum = 0;        for (int i = 0; i < 10; i++) {            sum += i;            if (sum > 10) {                System.out.println("id:" + id + " ==> " + sum);                Thread.yield();            }        }    }    public static void main(String [] args) {        for (int i = 1; i <= 10; i++) {            Thread thread = new Thread(new Priority());            thread.setPriority(i);            thread.start();        }    }}output://id:1 ==> 15id:4 ==> 15id:1 ==> 21id:6 ==> 15id:3 ==> 15id:2 ==> 15id:6 ==> 21id:7 ==> 15id:1 ==> 28id:4 ==> 21id:10 ==> 15id:5 ==> 15id:10 ==> 21id:4 ==> 28id:1 ==> 36id:7 ==> 21id:6 ==> 28id:9 ==> 15id:3 ==> 21id:2 ==> 21id:8 ==> 15id:2 ==> 28id:9 ==> 21id:3 ==> 28id:6 ==> 36id:7 ==> 28id:1 ==> 45id:4 ==> 36id:10 ==> 28id:5 ==> 21id:4 ==> 45id:7 ==> 36id:6 ==> 45id:3 ==> 36id:9 ==> 28id:2 ==> 36id:8 ==> 21id:9 ==> 36id:3 ==> 45id:7 ==> 45id:5 ==> 28id:10 ==> 36id:5 ==> 36id:9 ==> 45id:8 ==> 28id:2 ==> 45id:8 ==> 36id:5 ==> 45id:10 ==> 45id:8 ==> 45

线程优先级按照1 到10排列(与平台有关),1 的优先级最高.
通常在操作平台未知的情况下,会使用Thread.MAX_PRIORITY, Thread.MIN_PRIORITY, 和 Thread.NORM_PRIORITY.

0 0
原创粉丝点击