Java多线程,Thread和Runnable究竟该用哪个
2017-04-10 16:03
204 查看
很久没写博客了,内心有一丝罪恶感。其中一个原因是最近做的一些东西不适合在博客上公开。
今天抽空来说说Java多线程中的一个小话题,也是新人经常会遇到的。起因是我在给新人找培训资料的时候,在网上看到了很多Thread和Runnable究竟该用哪个的讨论,绝大多数这类文章都是同一个祖师爷的,都用了窗口卖票的例子。以下地址是一篇比较有代表性的(搞笑的是连Runnable都拼错了):
http://www.oschina.net/question/565065_86563
可见新人们在网上找到的资料都是些这种东西,真让人忧虑啊。这篇博文槽点太多,我就不一一喷了,我们来把这篇认为正确的“最终写法”拿出来实际编一编试试(我做了一处小改动,因为原始写法线程还有死循环问题,另外把票数从100张改到了10张,便于分析)。
初看运行结果是正常的,3个窗口卖10张票。可是多运行几次问题就来了,可能我脸比较黑,只跑了4遍就遇到了一次这样的结果:
Thread-0 is saling ticket 10
Thread-0 is saling ticket 8
Thread-2 is saling ticket 9
Thread-2 is saling ticket 6
Thread-1 is saling ticket 10
Thread-1 is saling ticket 4
Thread-2 is saling ticket 5
Thread-2 is saling ticket 2
Thread-2 is saling ticket 1
Thread-0 is saling ticket 7
Thread-1 is saling ticket 3
是的,编号10的票被卖了2次。为什么会这样,相信对多线程有了解的程序员都应该知道,--操作符内部的实现并不是原子的。解决方法很简单,一是用synchronized这种内置锁,二是用AtomicInteger这样的concurrent包里封装好的元素,简洁起见我用第二种实现如下:
以上代码还说明一个问题,这类博文中说的“Runnable可以用来在多线程间共享对象,而Thread不能共享对象”,纯属无稽之谈,Thread本身就实现了Runnable,不会导致能不能共享对象这种区别。两者最大(甚至可以说唯一,因为其他差异对新人来说无关紧要)的区别是Thread是类而Runnable是接口,至于用类还是用接口,取决于继承上的实际需要。
最后回答标题的问题:Thread和Runnable究竟该用哪个?我的建议是都不用。因为Java这门语言发展到今天,在语言层面提供的多线程机制已经比较丰富且高级,完全不用在线程层面操作。直接使用Thread和Runnable这样的“裸线程”元素比较容易出错,还需要额外关注线程数等问题。建议:
简单的多线程程序,使用Executor。
简单的多线程,但不想关注线程层面因素,又熟悉Java8的:使用Java8的并行流,它底层基于ForkJoinPool,还能享受函数式编程的快捷。
复杂的多线程程序,使用一个Actor库,首推Akka。
今天抽空来说说Java多线程中的一个小话题,也是新人经常会遇到的。起因是我在给新人找培训资料的时候,在网上看到了很多Thread和Runnable究竟该用哪个的讨论,绝大多数这类文章都是同一个祖师爷的,都用了窗口卖票的例子。以下地址是一篇比较有代表性的(搞笑的是连Runnable都拼错了):
http://www.oschina.net/question/565065_86563
可见新人们在网上找到的资料都是些这种东西,真让人忧虑啊。这篇博文槽点太多,我就不一一喷了,我们来把这篇认为正确的“最终写法”拿出来实际编一编试试(我做了一处小改动,因为原始写法线程还有死循环问题,另外把票数从100张改到了10张,便于分析)。
class ThreadTest implements Runnable { private int tickets = 10; public void run() { while (true) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + " is saling ticket " + tickets--); } else { break; } } } } public class ThreadDemo1 { public static void main(String[] args) { ThreadTest t = new ThreadTest(); new Thread(t).start(); new Thread(t).start(); new Thread(t).start(); } }
初看运行结果是正常的,3个窗口卖10张票。可是多运行几次问题就来了,可能我脸比较黑,只跑了4遍就遇到了一次这样的结果:
Thread-0 is saling ticket 10
Thread-0 is saling ticket 8
Thread-2 is saling ticket 9
Thread-2 is saling ticket 6
Thread-1 is saling ticket 10
Thread-1 is saling ticket 4
Thread-2 is saling ticket 5
Thread-2 is saling ticket 2
Thread-2 is saling ticket 1
Thread-0 is saling ticket 7
Thread-1 is saling ticket 3
是的,编号10的票被卖了2次。为什么会这样,相信对多线程有了解的程序员都应该知道,--操作符内部的实现并不是原子的。解决方法很简单,一是用synchronized这种内置锁,二是用AtomicInteger这样的concurrent包里封装好的元素,简洁起见我用第二种实现如下:
import java.util.concurrent.atomic.AtomicInteger; //public class MyThread extends Thread { //两种写法一样 public class MyThread implements Runnable{ private AtomicInteger tickets = new AtomicInteger(10); @Override public void run() { while (true) { int ticketnum; if ((ticketnum = tickets.getAndDecrement()) > 0) { System.out.println(Thread.currentThread().getName() + " is saling ticket: " + ticketnum); } else { break; } } } public static void main(String[] args) { MyThread mt = new MyThread(); Thread t1 = new Thread(mt, "Win1"); Thread t2 = new Thread(mt, "Win2"); Thread t3 = new Thread(mt, "Win3"); t1.start(); t2.start(); t3.start(); } }
以上代码还说明一个问题,这类博文中说的“Runnable可以用来在多线程间共享对象,而Thread不能共享对象”,纯属无稽之谈,Thread本身就实现了Runnable,不会导致能不能共享对象这种区别。两者最大(甚至可以说唯一,因为其他差异对新人来说无关紧要)的区别是Thread是类而Runnable是接口,至于用类还是用接口,取决于继承上的实际需要。
最后回答标题的问题:Thread和Runnable究竟该用哪个?我的建议是都不用。因为Java这门语言发展到今天,在语言层面提供的多线程机制已经比较丰富且高级,完全不用在线程层面操作。直接使用Thread和Runnable这样的“裸线程”元素比较容易出错,还需要额外关注线程数等问题。建议:
简单的多线程程序,使用Executor。
简单的多线程,但不想关注线程层面因素,又熟悉Java8的:使用Java8的并行流,它底层基于ForkJoinPool,还能享受函数式编程的快捷。
复杂的多线程程序,使用一个Actor库,首推Akka。
相关文章推荐
- java多线程 基础(二) Thread Runnable
- Java 多线程的Thread类和Runnable接口
- Java多线程--认识多线程(Thread、Runnable)
- Java学习从菜鸟变大鸟之三 多线程中Thread 和Runnable的区别与运用
- 【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)
- java多线程学习1-继承Thread类和实现Runnable接口来创建线程
- Java对多线程的支持(一) - Thread类、Runnable接口、Daemon线程、线程同步
- Java 多线程的Thread类和Runnable接口(转载)
- Java 多线程的Thread类和Runnable接口
- Java多线程-(4)线程的创建、启动、Runnable和Thread
- java 三种多线程的创建方法Thread,Handler,Runnable
- java 多线程 Thread和Runnable的区别
- java创建多线程的两种方法(继承Thread类/实现Runnable接口)
- Java对多线程的支持(一) - Thread类、Runnable接口、Daemon线程、线程同步
- Java【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- 学习java多线程的笔记1--Thread(Runnable t)与重写run()方法等
- JAVA基础(多线程Thread和Runnable的使用区别
- Java 多线程的Thread类和Runnable接口
- [原]Java多线程编程学习笔记之五:Runnable和Thread实现多线程的区别(含代码)
- java中用Runnable和Thread实现多线程,多线程间的同步和死锁。