当前位置:K88软件开发文章中心编程语言JavaJava01 → 文章内容

JAVA并发编程1_多线程的实现方式

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-4 7:54:36

-->

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_多线程的实现方式