java笔记(五):多线程--Thread和Runnable
2017-06-21 04:24
375 查看
多线程是java高效利用CPU的关键。
首先要明白线程与进程的区别。
进程:程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程。
进程的特点:通过进程控制块(PCB)唯一的标识某个进程。同时进程占据着相应的资源(例如包括cpu的使用 ,轮转时间以及一些其它设备的权限)。是系统进行资源分配和调度的一个独立单位。有多种状态(就绪,执行,阻塞)
线程:进程中执行的一个任务。
线程的特点:需要较少的资源,和同一进程的其他线程共享进程的资源。是CPU调度和分派的基本单位(它是比进程更小的能独立运行的基本单位)。有多种状态。
举例来说,打开了一个word是打开了一个进程,一次换行操作是该进程中的一个线程。java代码的一个完整执行是一次进程的执行,代码中的多线程任务就是线程。
创建一个多线程,有三种方法:
通过实现 Runnable 接口;
通过继承 Thread 类本身;
通过 Callable 和 Future 创建线程。
官方解释说,Runnable应该被任何希望由Thread执行的类所继承。它提供了一种方法使得即使不继承Thread,也可以执行多线程。
另外,Runnable可以通过实例化一个Thread然后把自己传给Thread来执行多线程,而不是靠继承Thread。
多数情况下,当我们只是想覆盖run()方法时,使用Runnable是最好的选择。
单独的Runnable是没法实现多线程,无论如何都绕不开Thread这个类。
另一种更具有封装性的使用Runnable接口的方法如下:
上述代码同样可以完成多线程的功能。
java多线程无论如何多绕不开Thread,下面简单剖析下Thread的源码。
上述程序会每隔1s进行一次输出。
join方法的源码如下:
重点在于wait()。可能许多人有疑问,为什么调用wait()方法后,线程对象仍可以继续执行,实际上。wait()方法的调用是在join()方法里,也就是说在主线程中(理解这一点很重要),而线程对象的run()方法并不受while()循环的影响,反而是主线程因为循环而停止执行。因此,直到线程对象执行完,isAlive()方法返回false,主线程才跳出while,重新执行。
补充一句,线程的join方法常用于有明确先后顺序需求的场景。
通过setDaemon(true)来设置。
参考程序如下:
上述代码的输出结果如下:
由此说明了几个问题:
主线程结束后,守护线程也都停止了
守护线程被打断后finally的代码块未被执行
首先要明白线程与进程的区别。
进程:程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程。
进程的特点:通过进程控制块(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基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别
- java多线程总结笔记1——Runnable和Thread
- Java多线程高并发学习笔记(一)——Thread&Runnable
- 学习java多线程的笔记1--Thread(Runnable t)与重写run()方法等
- Java 多线程的Thread类和Runnable接口(转载)
- Java 多线程的Thread类和Runnable接口
- Java对多线程的支持(一) - Thread类、Runnable接口、Daemon线程、线程同步
- java 多线程 Thread与Runnable的区别
- [原]Java多线程编程学习笔记之五:Runnable和Thread实现多线程的区别(含代码)
- Java对多线程的支持(一) - Thread类、Runnable接口、Daemon线程、线程同步
- Java多线程--认识多线程(Thread、Runnable)
- JAVA基础(多线程Thread和Runnable的使用区别
- Java【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- java笔记之Runnable和Thread的区别
- [原]Java多线程编程学习笔记之五:Runnable和Thread实现多线程的区别(含代码)
- Java:多线程,分别用Thread、Runnable、Callable实现线程
- java 三种多线程的创建方法Thread,Handler,Runnable
- java 三种多线程的创建方法Thread,Handler,Runnable
- java多线程 基础(二) Thread Runnable
- Java 多线程的Thread类和Runnable接口