JAVA中创建线程的两种方式:继承Thread或实现Runnable接口。
1 继承Thread类,重写run方法:
/**
* 实现线程的第一种方式 :继承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对象。
/**
* 实现线程的第二种方式 :实现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接口的。
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();
public synchronized void start() {
……
boolean started = false;
try {
start0();
started = true;
} finally {
……
}
}
private native void start0();
总之,会辗转调用到run方法:
/**
* 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.
* <p>
* Subclasses of <code>Thread</code> 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方法。
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字段改为staticclass 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
出现这样的结果就是因为多线程并发的不可控性,关于线程同步这个问题会在后面继续探讨。