您的位置:首页 > 编程语言 > Java开发

java笔记(五):多线程--Thread和Runnable

2017-06-21 04:24 375 查看
多线程是java高效利用CPU的关键。

首先要明白线程与进程的区别。

进程:程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程。

进程的特点:通过进程控制块(PCB)唯一的标识某个进程。同时进程占据着相应的资源(例如包括cpu的使用 ,轮转时间以及一些其它设备的权限)。是系统进行资源分配和调度的一个独立单位。有多种状态(就绪,执行,阻塞)

线程:进程中执行的一个任务。

线程的特点:需要较少的资源,和同一进程的其他线程共享进程的资源。是CPU调度和分派的基本单位(它是比进程更小的能独立运行的基本单位)。有多种状态。

举例来说,打开了一个word是打开了一个进程,一次换行操作是该进程中的一个线程。java代码的一个完整执行是一次进程的执行,代码中的多线程任务就是线程。

创建一个多线程,有三种方法:

通过实现 Runnable 接口;

通过继承 Thread 类本身;

通过 Callable 和 Future 创建线程。

Runnable

首先还是剖析下Runnable源码:

@FunctionalInterface
public interface Runnable {
/**
* 当使用实现该接口的类创建Thread时,开启该线程会使
* run()方法被一个分开的线程调用,以实现多线程的目的
*/
public abstract void run();
}


官方解释说,Runnable应该被任何希望由Thread执行的类所继承。它提供了一种方法使得即使不继承Thread,也可以执行多线程。

另外,Runnable可以通过实例化一个Thread然后把自己传给Thread来执行多线程,而不是靠继承Thread。

多数情况下,当我们只是想覆盖run()方法时,使用Runnable是最好的选择。

/** 以下
* 为可能的结果之一
* thread2:0
* thread1:0
* thread2:1
* thread1:1
* thread2:2
* thread1:2
* thread1:3
* thread2:3
* thread1:4
* thread2:4
* 单纯的执行new ThreadRunnable("thread3").run()得不到多线
* 的效果
*/
new Thread(new ThreadRunnable("thread1")).start();
new Thread(new ThreadRunnable("thread2")).start();
//new ThreadRunnable("thread3").run();
//new ThreadRunnable("thread4").run();

class ThreadRunnable implements Runnable{
private String name;
public ThreadRunnable(String name){
this.name = name;
}
public void run(){
for(int i = 0;i<5;i++){
System.out.println(name+":"+i);
}
}
}


单独的Runnable是没法实现多线程,无论如何都绕不开Thread这个类。

另一种更具有封装性的使用Runnable接口的方法如下:

//new Thread(new ThreadRunnable("thread1")).run();
//new Thread(new ThreadRunnable("thread2")).run();
new ThreadRunnable("thread3").start();
new ThreadRunnable("thread4").start();

class ThreadRunnable implements Runnable{
private String name;

public ThreadRunnable(String name){
this.name = name;
}
public void run(){
for(int i = 0;i<10;i++){
System.out.println(name+":"+i);
}
}
public void start(){
new Thread(this, name).start();
}
}


上述代码同样可以完成多线程的功能。

Thread

继承Thread并重写run()是实现多线程的另一种方法。

java多线程无论如何多绕不开Thread,下面简单剖析下Thread的源码。

public class Thread implements Runnable {
private int priority;  //优先级
private boolean daemon = false;  //是不是守护线程
private Runnable target;


sleep()

该方法导致当前正在执行的线程休眠指定的毫秒数.

ThreadTest t = new ThreadTest("thread0");
t.start();
Thread.sleep(10000);
t.flag = false;

class ThreadTest extends Thread{
private String name;
private int i = 0;
boolean flag = true;
public ThreadTest(String name){
this.name = name;
}
public void run(){
while(flag){
try{
Thread.sleep(1000);
} catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(name+":"+i++);
}
}
}


上述程序会每隔1s进行一次输出。

join()

该方法的作用是使得线程对象先执行完(或者先执行指定的毫秒),然后主程序在接着执行。

/**
*结果如下:
main
thread
main
thread
main
thread
main
thread
main
thread
thread
thread
thread
thread
thread
main
main
main
main
main
*/

ThreadTest t = new ThreadTest();
t.start();
for(int i = 0;i<10;i++){
if(i == 5){
t.join();
}
try{
Thread.sleep(1000);
} catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("main");
}

class ThreadTest extends Thread{
private int i = 0;
public void run(){
for(int i = 0;i<10;i++){
try{
Thread.sleep(1000);
} catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread");
}
}
}


join方法的源码如下:

public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;

if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}

if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}


重点在于wait()。可能许多人有疑问,为什么调用wait()方法后,线程对象仍可以继续执行,实际上。wait()方法的调用是在join()方法里,也就是说在主线程中(理解这一点很重要),而线程对象的run()方法并不受while()循环的影响,反而是主线程因为循环而停止执行。因此,直到线程对象执行完,isAlive()方法返回false,主线程才跳出while,重新执行。

补充一句,线程的join方法常用于有明确先后顺序需求的场景。

ThreadTest t1 = new ThreadTest();
ThreadTest t2 = new ThreadTest();
ThreadTest t3 = new ThreadTest();
//t1先执行完后执行t2,在执行t3。
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();


yield()

源码的注释解释,该方法表明当前线程愿意放弃cpu给同等优先级的其他线程。但也可能该线程放弃cpu在进入到可执行状态后马上又被执行。yield执行后线程进入就绪状态。

守护线程

Daemon Thread,又译为后台线程,是指在程序运行时在后台提供一种通用服务的线程,当所有非后台线程结束时,程序就会终止。这意味着,程序的终止不依赖于守护线程,程序终止的同时会杀死所有守护线程。

通过setDaemon(true)来设置。

参考程序如下:

public class Test01{
public static void main(String[] args) throws InterruptedException {
for(int i=0;i<5;i++){
Thread t = new Thread(new DaemonThread());
t.setDaemon(true);
t.start();
}
TimeUnit.MILLISECONDS.sleep(180);
}
}
class DaemonThread implements Runnable{

public void run() {
try{
while (true){
TimeUnit.MILLISECONDS.sleep(100);
System.out.println(Thread.currentThread()+" "+this);
}
} catch (InterruptedException e) {
System.out.println("sleep interrupted");
}finally {
System.out.println(Thread.currentThread()+" over");
}
}
}


上述代码的输出结果如下:

Thread[Thread-4,5,main] DaemonThread@56384cb7
Thread[Thread-0,5,main] DaemonThread@546431f0
Thread[Thread-1,5,main] DaemonThread@383a0ba
Thread[Thread-2,5,main] DaemonThread@95458f7
Thread[Thread-3,5,main] DaemonThread@1857637d


由此说明了几个问题:

主线程结束后,守护线程也都停止了

守护线程被打断后finally的代码块未被执行
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 多线程