创建线程的方法
2016-09-18 16:32
190 查看
从操作系统的角度来看,线程是CPU调度的最小单位——可以理解为一种资源、权利,即能够获得CPU的使用权
网上很多文章中说java创建线程的方法有两种(Thread Runnable)或三种(外加Callable Future),这种说法不能说是错误的,但是很容易误导人,实际上,创建线程(拥有CPU使用权的线程)只有一种方法,那就是继承Thread类。实现Runnable(包括Callable Future)准确地说,不是线程,而是任务,站在操作系统的角度来看,一个类实现类Runnable,并没有立刻获得能够使用CPU的权利,来看一下Runnable和Thread的源码
注释我去掉了一些,从Thread.run()方法来看,Runnable要是能运行的话——获得CPU的使用权,那么要放在Thread的构造函数中,否则不执行,然后看Thread.start()方法,其中调用了start0()方法,该方法是个本地方法,本地方法的实现要去看源码,openjdk8的源码下载地址,因为最终是要看cpp的,而我不会cpp,引用另外两篇博文:调用Thread.start()方法,JVM(Hotspot)会调用操作系统的API来创建一个操作系统线程,此时一个Java线程对象就与一个操作系统线程一一对应。
那么为什么需要Runnable接口呢?只有Thread不就够类么,重写run()方法就行了,网上很多都说Runnable是接口,而Thread是类,实现Runnable能够避免Java单根继承带来的局限,且实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享
我认为都没说到点子上,重点是:调用Thread.start()时,是通过操作系统来创建线程的,而创建线程必然是运行在内核态,那么就一定有上下文切换,从而带来额外的时间和空间开销。如果将线程(Thread对象)看作插槽,它代表了资源——能够使用CPU(其实还有寄存器等存储资源),Runnable对象看作插头——要运行的任务,因为创建、撤销插槽开销很大,那么可以先创建一组插槽,什么任务需要运行,就插入相应的插头,就能够避免频繁的创建、撤销,避免上下文切换,从而提高性能,没错,就是线程池的思想
很多地方的线程和任务并没有分清楚,比如这里,若是站在java程序员的角度,Runnable就是一个线程,为啥?我要在执行多个任务,实现Runnable接口,不就是线程么
注意Runnable使用了匿名内部类,
需要注意的是,以上两个程序中,线程的启动顺序是确定的,但是执行顺序是不确定的,因为是并发执行而不是顺序执行的,JVM和操作系统一起决定了线程的执行顺序,下图分别为Runnable在windown7+jdk1.7.0_67和ubuntu16.04+jdk1.8.0_101上运行的结果
参考资料:
1. java编程思想
2. http://www.ticmy.com/?p=230
3. http://blog.csdn.net/jeffhtlee/article/details/12751825
网上很多文章中说java创建线程的方法有两种(Thread Runnable)或三种(外加Callable Future),这种说法不能说是错误的,但是很容易误导人,实际上,创建线程(拥有CPU使用权的线程)只有一种方法,那就是继承Thread类。实现Runnable(包括Callable Future)准确地说,不是线程,而是任务,站在操作系统的角度来看,一个类实现类Runnable,并没有立刻获得能够使用CPU的权利,来看一下Runnable和Thread的源码
@FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * * @see java.lang.Thread#run() */ public abstract void run(); }
public class Thread implements Runnable { private ThreadGroup group; public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0(); /** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * @see #start() */ @Override public void run() { if (target != null) { target.run(); } }
注释我去掉了一些,从Thread.run()方法来看,Runnable要是能运行的话——获得CPU的使用权,那么要放在Thread的构造函数中,否则不执行,然后看Thread.start()方法,其中调用了start0()方法,该方法是个本地方法,本地方法的实现要去看源码,openjdk8的源码下载地址,因为最终是要看cpp的,而我不会cpp,引用另外两篇博文:调用Thread.start()方法,JVM(Hotspot)会调用操作系统的API来创建一个操作系统线程,此时一个Java线程对象就与一个操作系统线程一一对应。
那么为什么需要Runnable接口呢?只有Thread不就够类么,重写run()方法就行了,网上很多都说Runnable是接口,而Thread是类,实现Runnable能够避免Java单根继承带来的局限,且实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享
我认为都没说到点子上,重点是:调用Thread.start()时,是通过操作系统来创建线程的,而创建线程必然是运行在内核态,那么就一定有上下文切换,从而带来额外的时间和空间开销。如果将线程(Thread对象)看作插槽,它代表了资源——能够使用CPU(其实还有寄存器等存储资源),Runnable对象看作插头——要运行的任务,因为创建、撤销插槽开销很大,那么可以先创建一组插槽,什么任务需要运行,就插入相应的插头,就能够避免频繁的创建、撤销,避免上下文切换,从而提高性能,没错,就是线程池的思想
很多地方的线程和任务并没有分清楚,比如这里,若是站在java程序员的角度,Runnable就是一个线程,为啥?我要在执行多个任务,实现Runnable接口,不就是线程么
1.1实现Runnable接口
public class MyRunnable{ public static void main(String[] args) { Thread[] ths = new Thread[3]; for (int i = 0; i < 3; i++) { ths[i] = new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 5; j++) System.out.print(j); System.out.print(" "); } }); } for (Thread th: ths) th.start(); } }
注意Runnable使用了匿名内部类,
1.2继承自Thread类
public class MyThread extends Thread{ @Override public void run(){ for (int i = 0; i < 5; i++) System.out.print(i); System.out.print(" "); } public static void main(String[] args){ MyThread[] thx = { new MyThread(), new MyThread(), new MyThread(), }; for (MyThread th: thx){ th.start(); } } }
需要注意的是,以上两个程序中,线程的启动顺序是确定的,但是执行顺序是不确定的,因为是并发执行而不是顺序执行的,JVM和操作系统一起决定了线程的执行顺序,下图分别为Runnable在windown7+jdk1.7.0_67和ubuntu16.04+jdk1.8.0_101上运行的结果
参考资料:
1. java编程思想
2. http://www.ticmy.com/?p=230
3. http://blog.csdn.net/jeffhtlee/article/details/12751825
相关文章推荐
- 在子线程中创建线程的方法
- 在子线程中创建线程的方法
- 多线程中创建线程的几种方法
- Java6学习笔记55——多线程编程——线程的创建方法1
- 小结“线程间操作无效: 从不是创建控件的线程访问它” 错误的解决方法
- 线程的创建方法
- 【转】线程间操作无效: 从不是创建控件“”的线程访问它~~~的解决方法
- 开发笔记:创建Java线程的两种方法
- Java6学习笔记56——多线程编程——线程的创建方法2
- 创建线程的方法一:继承Thread类
- 创建线程调用类成员函数的方法。
- vc创建一个线程的方法:
- 线程间操作无效: 从不是创建控件“”的线程访问它~~~的解决方法~
- 从“非创建控件线程”中调用控件的基本方法(1)
- “线程间操作无效:从不是创建控件“XX”的线程访问它”的解决方法
- 孙鑫VC学习笔记:第十五讲 线程创建方法
- 孙鑫VC学习笔记:第十五讲 (二) 线程创建方法
- 小结“线程间操作无效: 从不是创建控件的线程访问它” 错误的解决方法
- 线程间操作无效: 从不是创建控件“”的线程访问它的解决方法
- 线程间操作无效: 从不是创建控件“”的线程访问它~~~的解决方法~