- ·上一篇文章:Java中static、final用法及区别
- ·下一篇文章:JAVA并发编程2_线程安全&内存模型
JAVA并发编程1_多线程的实现方式
-->
JAVA中创建线程的两种方式:继承Thread或实现Runnable接口。
1 继承Thread类,重写run方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** * 实现线程的第一种方式 :继承Thread * 实现数据共享需要设置属性为静态 * @author qhyuan1992 * */ class MyThread extends Thread{ private int count;// static public MyThread(String id){ super(id); } public void run() { while (count < 5) { count ++; System.out.println(currentThread() + "--->" + count); } } } class Test{ public static void main(String[] args) { Thread t1 = new MyThread("thread_1"); Thread t2 = new MyThread("thread_2"); t1.start(); t2.start(); } } // output(未共享资源) //Thread[thread_1,5,main]--->1 //Thread[thread_2,5,main]--->1 //Thread[thread_1,5,main]--->2 //Thread[thread_1,5,main]--->3 //Thread[thread_1,5,main]--->4 //Thread[thread_2,5,main]--->2 //Thread[thread_1,5,main]--->5 //Thread[thread_2,5,main]--->3 //Thread[thread_2,5,main]--->4 //Thread[thread_2,5,main]--->5 |
2 实现Runnable接口,重写run方法,作为参数传给Thread对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /** * 实现线程的第二种方式 :实现Runnable接口 * 实现数据共享 * @author qhyuan1992 */ class MyRunnable implements Runnable{ private int count; public void run() { while (count < 5) { count ++; System.out.println(Thread.currentThread() + "--->" + count); } } public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread t1 = new Thread(runnable); Thread t2 = new Thread(runnable); t1.start(); t2.start(); } } // output(使用同一个Runnable对象构造Thread对象实现资源共享) //Thread[Thread-0,5,main]--->2 //Thread[Thread-1,5,main]--->2 //Thread[Thread-1,5,main]--->3 //Thread[Thread-1,5,main]--->4 //Thread[Thread-1,5,main]--->5 |
两种方式有何区别?查看源代码可以看到,Thread类是实现了Runnable接口的。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Thread implements Runnable { … /* What will be run. */ private Runnable target; // target就是我们使用第二种方法的时候传递的runnable对象 /* The group of this thread */ private ThreadGroup group; … public Thread(Runnable target) {//第二种使用线程的方式的构造函数 init(null, target, "Thread-" + nextThreadNum(), 0); } } |
当调用Thread类的start()方法时,会调用本地方法start0();
1 2 3 4 5 6 7 8 9 10 11 | public synchronized void start() { …… boolean started = false; try { start0(); started = true; } finally { …… } } private native void start0(); |
总之,会辗转调用到run方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * If this thread was constructed using a separate * Runnable run object, then that * Runnable object's run method is called; * otherwise, this method does nothing and returns. * * Subclasses of Thread should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } |
如果使用Runnable来构造Thread的话,将调用Runnable对象的run方法;否则,什么也不做。但通过继承的方式来实现线程,由于多态根本没有执行Thread类中的run方法,就会执行重写的run方法。
例如:要是继承自Thread实现了run方法,也通过了Runnable来构造Thread结果会执行哪个run方法呢?答案是确定的:会执行我们写在继承自Thread类中的run方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | class MyRunnable implements Runnable{ public void run() { System.out.println("MyRunnable"); } } class MyThread extends Thread{ public MyThread(Runnable r){ super(r); } public void run() { System.out.println("MyThread"); } } class Test{ public static void main(String[] args) { Thread t = new MyThread(new MyRunnable()); t.start(); } } // output: //MyThread |
到底使用Thread还是Runnable?
实现Runnable接口比继承Thread类所具有的优势:
- 1.适合多个相同的程序代码的线程去处理同一个资源,继承Thread的需要将共享的资源设置为static。
- 2.可以避免java中的单继承的限制
- 3.增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
将第一个代码的count字段改为static
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class MyThread extends Thread{ private static int count;// static public MyThread(String id){ super(id); } public void run() { while (count < 5) { count ++; System.out.println(currentThread() + "--->" + count); } } } class Test{ public static void main(String[] args) { Thread t1 = new MyThread("thread_1"); Thread t2 = new MyThread("thread_2"); t1.start(); t2.start(); } } // output (结果不确定) //Thread[thread_1,5,main]--->2 //Thread[thread_2,5,main]--->2 //Thread[thread_2,5,main]--->3 //Thread[thread_2,5,main]--->4 //Thread[thread_2,5,main]--->5 |
可以看到使用static的方式也可以共享资源,和使用第二种方式实现多线程一样,前提是创建Thread的Runnable对象是同一个。
细心一点可以看到打印的结构不是我们所预期的,例如:
//Thread[thread_1,5,main]--->2//Thread[thread_2,5,main]--->2
出现这样的结果就是因为多线程并发的不可控性,关于线程同步这个问题会在后面继续探讨。
JAVA并发编程1_多线程的实现方式