创建并运行Java运行时代码的三种方式
2017-08-15 15:23
288 查看
原文地址: http://www.tianshouzhi.com/api/tutorials/mutithread/62
1 概述
在Java中,创建线程
第一种:继承
第二种:实现
第三种:实现
在讲解具体的代码如何做之前,我们先来讲解一些概念上的问题。
1.1 创建线程与创建线程运行时代码的区别
一个很常见的错误是,将创建
创建线程的方式只有一种,就是创建Thread对象的实例,创建线程运行时代码就是以上提到的三种方式。
对于Runnable和Callable接口,如果我们实现它们,主要就是为了实现接口中定义的方法,以便线程执行时回调,而实现的方法中的具体内容,就是我们所说的
对于Thread,其本身是一个线程对象,不过由于其也实现了Runable接口,因此其本身是将
千万不要以为是我在大惊小怪,因为这种概念上的误解实在太常见了,引用百度中一段搜索内容作为说明:
可以看到,当我搜索"创建java线程的方式"的时候,排在前几位的都是2种或者3种(2种的情况不包含实现Callable这种方式,因为这种方式是后来引入的),这个问题就严重了,很多人都会因此而受到误解。
不过现在你已经完全了解二者的区别了,这值得庆祝一下:
2 创建线程运行时代码
现在我们知道,要编写一个并发应用,我们需要创建线程对象,同时还要创建线程运行时代码。因为Callable的方式暂时不进行讲解。因此我们主要关注的是:
创建Thread子类的一个实例并重写
创建类的时候实现
2.1 创建Thread的子类,覆写run方法
创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。这种方式是将创建线程对象和创建线程运行时代码合并为一个过程。例子如下:
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
}
}
可以用如下方式创建并运行上述Thread子类
MyThread myThread = new MyThread();
myTread.start();
一旦线程启动后start方法就会立即返回,而不会等待到run方法执行完毕才返回。就好像run方法是在另外一个cpu上执行一样。当run方法执行后,将会打印出字符串MyThread running。
你也可以如下创建一个Thread的匿名子类:
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
}
};
thread.start();
当新的线程的run方法执行以后,计算机将会打印出字符串”Thread Running”。
2.2 实现Runnable接口
第二种编写线程执行代码的方式是新建一个实现了
public class MyRunnable implements Runnable {
public void run(){
System.out.println("MyRunnable running");
}
}
为了使线程能够执行run()方法,需要在Thread类的构造函数中传入 MyRunnable的实例对象。示例如下:
Thread thread = new Thread(new MyRunnable());
thread.start();
当线程运行时,它将会调用实现了Runnable接口的run方法。上例中将会打印出”MyRunnable running”。
同样,也可以创建一个实现了Runnable接口的匿名类,如下所示:
Runnable myRunnable = new Runnable(){
public void run(){
System.out.println("Runnable running");
}
}
Thread thread = new Thread(myRunnable);
thread.start();
3、创建子类还是实现Runnable接口?
对于这两种方式哪种好并没有一个确定的答案,它们都能满足要求。就我个人意见,我更倾向于实现Runnable接口这种方法。因为Java中有一个
4、常见错误:调用run()方法而非start()方法
创建并运行一个线程所犯的常见错误是调用线程的run()方法而非start()方法,如下所示:
Thread newThread = new Thread(MyRunnable());
newThread.run(); //should be start();
起初你并不会感觉到有什么不妥,因为run()方法的确如你所愿的被调用了。但是,事实上,run()方法并非是由刚创建的新线程所执行的,而是被 创建新线程的当前线程所执行了。也就是被执行上面两行代码的线程所执行的。想要让创建的新线程执行run()方法,必须调用新线程的start方法。
线程名
当创建一个线程的时候,可以给线程起一个名字。它有助于我们区分不同的线程。例如:如果有多个线程写入System.out,我们就能够通过线程名容易的找出是哪个线程正在输出。例子如下:
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable, "New Thread");
thread.start();
System.out.println(thread.getName());
需要注意的是,因为MyRunnable并非Thread的子类,所以MyRunnable类并没有getName()方法。可以通过以下方式得到当前线程的引用:
Thread.currentThread();
因此,通过如下代码可以得到当前线程的名字:
String threadName = Thread.currentThread().getName();
线程代码举例:
这里是一个小小的例子。首先输出执行main()方法线程名字。这个线程JVM分配的。然后开启10个线程,命名为1~10。每个线程输出自己的名字后就退出。
public class ThreadExample {
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());
for(int i=0; i<10; i++){
new Thread("" + i){
public void run(){
System.out.println("Thread: " + getName() + "running");
}
}.start();
}
}
}
需要注意的是,尽管启动线程的顺序是有序的,但是执行的顺序并非是有序的。也就是说,1号线程并不一定是第一个将自己名字输出到控制台的线程。这是因为线程是并行执行而非顺序的。Jvm和操作系统一起决定了线程的执行顺序,他和线程的启动顺序并非一定是一致的。
1 概述
在Java中,创建线程
运行时代码有三种方式。
第一种:继承
Thread类,覆写其
run方法,这种方式我们在之间的案例中已经见过。
第二种:实现
Runnable接口,实现
run方法,Thread类也实现了Runable接口。
第三种:实现
Callable接口,实现其
call方法,这种方式是在JDK1.5中的java并发包中引入的,因为本教程会有一个章节单独讲解Java并发包,所以在这里只是提一下有这种方式。
在讲解具体的代码如何做之前,我们先来讲解一些概念上的问题。
1.1 创建线程与创建线程运行时代码的区别
一个很常见的错误是,将创建
线程运行时代码和
创建线程混为一谈。在这里先给出一个结论:
创建线程的方式只有一种,就是创建Thread对象的实例,创建线程运行时代码就是以上提到的三种方式。
对于Runnable和Callable接口,如果我们实现它们,主要就是为了实现接口中定义的方法,以便线程执行时回调,而实现的方法中的具体内容,就是我们所说的
线程运行时代码。
对于Thread,其本身是一个线程对象,不过由于其也实现了Runable接口,因此其本身是将
创建线程对象和
线程运行时代码合为一体了。
千万不要以为是我在大惊小怪,因为这种概念上的误解实在太常见了,引用百度中一段搜索内容作为说明:
可以看到,当我搜索"创建java线程的方式"的时候,排在前几位的都是2种或者3种(2种的情况不包含实现Callable这种方式,因为这种方式是后来引入的),这个问题就严重了,很多人都会因此而受到误解。
不过现在你已经完全了解二者的区别了,这值得庆祝一下:
2 创建线程运行时代码
现在我们知道,要编写一个并发应用,我们需要创建线程对象,同时还要创建线程运行时代码。因为Callable的方式暂时不进行讲解。因此我们主要关注的是:
创建Thread子类的一个实例并重写
run方法
创建类的时候实现
Runnable接口
2.1 创建Thread的子类,覆写run方法
创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。这种方式是将创建线程对象和创建线程运行时代码合并为一个过程。例子如下:
public class MyThread extends Thread {
public void run(){
System.out.println("MyThread running");
}
}
可以用如下方式创建并运行上述Thread子类
MyThread myThread = new MyThread();
myTread.start();
一旦线程启动后start方法就会立即返回,而不会等待到run方法执行完毕才返回。就好像run方法是在另外一个cpu上执行一样。当run方法执行后,将会打印出字符串MyThread running。
你也可以如下创建一个Thread的匿名子类:
Thread thread = new Thread(){
public void run(){
System.out.println("Thread Running");
}
};
thread.start();
当新的线程的run方法执行以后,计算机将会打印出字符串”Thread Running”。
2.2 实现Runnable接口
第二种编写线程执行代码的方式是新建一个实现了
java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。下面给出例子:
public class MyRunnable implements Runnable {
public void run(){
System.out.println("MyRunnable running");
}
}
为了使线程能够执行run()方法,需要在Thread类的构造函数中传入 MyRunnable的实例对象。示例如下:
Thread thread = new Thread(new MyRunnable());
thread.start();
当线程运行时,它将会调用实现了Runnable接口的run方法。上例中将会打印出”MyRunnable running”。
同样,也可以创建一个实现了Runnable接口的匿名类,如下所示:
Runnable myRunnable = new Runnable(){
public void run(){
System.out.println("Runnable running");
}
}
Thread thread = new Thread(myRunnable);
thread.start();
3、创建子类还是实现Runnable接口?
对于这两种方式哪种好并没有一个确定的答案,它们都能满足要求。就我个人意见,我更倾向于实现Runnable接口这种方法。因为Java中有一个
线程池的概念。所谓线程池,可以理解为有一堆线程对象已经创建好了,那么其缺的就是
线程运行时代码。所以我们只需要提供了运行时代码就好了,因此实现Runable接口可能是更好的一种方式。
4、常见错误:调用run()方法而非start()方法
创建并运行一个线程所犯的常见错误是调用线程的run()方法而非start()方法,如下所示:
Thread newThread = new Thread(MyRunnable());
newThread.run(); //should be start();
起初你并不会感觉到有什么不妥,因为run()方法的确如你所愿的被调用了。但是,事实上,run()方法并非是由刚创建的新线程所执行的,而是被 创建新线程的当前线程所执行了。也就是被执行上面两行代码的线程所执行的。想要让创建的新线程执行run()方法,必须调用新线程的start方法。
线程名
当创建一个线程的时候,可以给线程起一个名字。它有助于我们区分不同的线程。例如:如果有多个线程写入System.out,我们就能够通过线程名容易的找出是哪个线程正在输出。例子如下:
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable, "New Thread");
thread.start();
System.out.println(thread.getName());
需要注意的是,因为MyRunnable并非Thread的子类,所以MyRunnable类并没有getName()方法。可以通过以下方式得到当前线程的引用:
Thread.currentThread();
因此,通过如下代码可以得到当前线程的名字:
String threadName = Thread.currentThread().getName();
线程代码举例:
这里是一个小小的例子。首先输出执行main()方法线程名字。这个线程JVM分配的。然后开启10个线程,命名为1~10。每个线程输出自己的名字后就退出。
public class ThreadExample {
public static void main(String[] args){
System.out.println(Thread.currentThread().getName());
for(int i=0; i<10; i++){
new Thread("" + i){
public void run(){
System.out.println("Thread: " + getName() + "running");
}
}.start();
}
}
}
需要注意的是,尽管启动线程的顺序是有序的,但是执行的顺序并非是有序的。也就是说,1号线程并不一定是第一个将自己名字输出到控制台的线程。这是因为线程是并行执行而非顺序的。Jvm和操作系统一起决定了线程的执行顺序,他和线程的启动顺序并非一定是一致的。
相关文章推荐
- java创建启动多线程的三种方式
- Java创建多线程的三种方式---Thread
- 【java】创建线程的三种方式浅析
- java创建线程的三种方式
- JSP嵌入java代码的三种标签方式
- java创建线程的三种方式
- jdt 创建java工程,生成代码,运行main方法
- 这里总结一下collection cell的三种创建方式的相关步骤,原形cell,xib,代码;还有对collection view的数据流flowlayout属性与collectionview的头底
- java中创建线程主要有三种方式
- dom4j解析xml的三种方式java代码
- java web 第2天:jsp中java代码三种交互方式
- [代码]DOM和LINQ to XML创建XML树的三种方式
- 并发编程学习总结(一) :java 创建线程的三种方式的优缺点和实例
- JavaWeb项目启动时,自动执行代码的三种方式(包含不占用tomcat启动时长的方式)
- java创建线程的三种方式及其对比
- java创建线程的三种方式(学习笔记)
- 创建android逐帧动画的两种方式 布局和java代码
- 三种Java创建线程的方式(Callable,FutureTask)
- java中内部类的创建四种情况,三种方式,及内部数据访问权限
- Java学习之多线程--创建线程的三种方式