码迷,mamicode.com
首页 > 编程语言 > 详细

启动多线程的两种情况比较

时间:2014-11-20 20:14:25      阅读:359      评论:0      收藏:0      [点我收藏+]

标签:style   blog   http   io   ar   color   使用   sp   for   

启动多线程有两种方式:(都是在主线程main线程下)

1. 使用同一个线程对象来启多个线程

2. 使用多个线程对象来启多个线程

 

这两种方式有什么区别呢?先贴上代码举例说明:

这是使用线程对象MyRunnable的同一个实例r来启动了两个线程

MyRunnable r = new MyRunnable();
Thread ta = new Thread(r,"Thread-A"); 
Thread tb = new Thread(r,"Thread-B"); 
ta.start(); 
tb.start();     

 

这是使用线程对象MyRunnable的两个不同的实例r来启动了两个线程

MyRunnable r1 = new MyRunnable();
MyRunnable r2 = new MyRunnable();
        
Thread ta = new Thread(r1,"Thread-A"); 
Thread tb = new Thread(r2,"Thread-B"); 
        
ta.start(); 
tb.start();

 

那么使用这两种方式的区别在哪里呢?我们紧接着看下面的代码的运行结果:

public class MyRunnable implements Runnable {
    private Foo foo =new Foo(); 

    public static void main(String[] args) {
        MyRunnable r = new MyRunnable();
        Thread ta = new Thread(r,"Thread-A"); 
        Thread tb = new Thread(r,"Thread-B"); 
        ta.start(); 
        tb.start(); 
        
        /*
        MyRunnable r1 = new MyRunnable();
        MyRunnable r2 = new MyRunnable();
        
        Thread ta = new Thread(r1,"Thread-A"); 
        Thread tb = new Thread(r2,"Thread-B"); 
        
        ta.start(); 
        tb.start();
        */
    } 

    public void run() {
            for (int i = 0; i < 3; i++) {
                this.fix(30);
                try {
                    Thread.sleep(1); 
                } catch (InterruptedException e) {
                    e.printStackTrace(); 
                } 
                System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());
            } 
    } 

    public int fix(int y) {
        return foo.fix(y);
    } 
}


class Foo {
    private int x = 100;

    public int getX() {
        return x;
    } 

    public int fix(int y) {
        x = x - y; 
        return x;
    } 
}

 

使用同一个线程对象启多个线程的运行结果:

Thread-B :当前foo对象的x值= 40
Thread-B :当前foo对象的x值= 10
Thread-A :当前foo对象的x值= -20
Thread-B :当前foo对象的x值= -50
Thread-A :当前foo对象的x值= -50
Thread-A :当前foo对象的x值= -80

 

使用多个线程对象启动多个线程的运行结果:

Thread-A :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= 10

 

我们可以看到在改变值的过程中,值串了。并且线程执行也是串的,两个线程之间在相互争抢执行。
(线程ta的run方法还没有执行完,tb的run方法争抢到了cpu资源从而执行)

在改变值的过程中,值改变是对的。线程执行是串的。(值没有串,是因为foo是私有变量,属于ta,tb所各自私有)

 

是我们不能允许的,因为值串了。
都出现的线程之间相互争抢的问题,就看我们的业务实现了。
使用多线程时,我们有时就是想启用多个线程同时去干不同的事情,这时它们相互争抢执行就是我们想要的。
有时,在多个线程同时访问一个方法时,我们希望当一个线程执行完这个方法后,再让其他的线程去执行,这时,我们就要避免线程之间相互争抢的问题,也就是使用同步锁机制来控制。

 

好,如果我们现在想要run()方法执行完了之后,其他线程才能再次进入run()方法来执行。我们用同步关键字synchronized来实现。如下:

同步方法:

synchronized public void run() {
        for (int i = 0; i < 3; i++) {
            this.fix(30);
            try {
                Thread.sleep(1); 
            } catch (InterruptedException e) {
                e.printStackTrace(); 
            } 
            System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());
        } 
    } 

同步块:

public void run() {
        synchronized(this){
            for (int i = 0; i < 3; i++) {
                this.fix(30);
                try {
                    Thread.sleep(1); 
                } catch (InterruptedException e) {
                    e.printStackTrace(); 
                } 
                System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());
            } 
        }
    } 

 

对于我们使用上面的同步方法和同步块都能得到如下的输出:

Thread-A :当前foo对象的x值= 70
Thread-A :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= -20
Thread-B :当前foo对象的x值= -50
Thread-B :当前foo对象的x值= -80

 

对于我们使用上面的同步方法和同步块却得到如下的输出:

Thread-A :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 70
Thread-A :当前foo对象的x值= 40
Thread-B :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= 10

 

的结果与我们预期的是一样的,但是却不如我们的预期,各个线程之间还是在相互争抢执行。

为什么呢?我们不是都已经使用synchronized同步了吗?

导致这个问题的根源就是对象锁的问题。

中使用同步方法时,线程ta,tb对应的对象锁都为MyRunnable的实例对象r,对象锁共享且唯一,所以起到了同步的作用。
同理,使用同步块时,ta,tb的对象锁也都是MyRunnable的实例对象r,故也能达到效果。

但对于不同的是,使用方法同步和块同步时,线程ta,tb对应的对象锁分别是各自的线程对象的实例,即ta-->r1,tb-->r2。故线程ta,tb分别持有各自的对象锁,所以达不到同步的效果。

 

如果换成如下代码执行

public void run() {
        synchronized("123"){
            for (int i = 0; i < 3; i++) {
                this.fix(30);
                try {
                    Thread.sleep(1); 
                } catch (InterruptedException e) {
                    e.printStackTrace(); 
                } 
                System.out.println(Thread.currentThread().getName() + " :当前foo对象的x值= " + foo.getX());
            } 
        }
    } 

我们得到如下结果:

Thread-A :当前foo对象的x值= 70
Thread-A :当前foo对象的x值= 40
Thread-A :当前foo对象的x值= 10
Thread-B :当前foo对象的x值= 70
Thread-B :当前foo对象的x值= 40
Thread-B :当前foo对象的x值= 10

 

这下就和我们的预期一样了。ta,tb线程都持有字符串"123"作为对象锁,ta,tb线程中的"123"都指向相同的内存地址,故对象锁相同且共享,故能达到同步效果。(为什么ta,tb中的"123"指向相同的内存地址,与String对象本身比较特殊有关,在此不赘述)

对于文章中的对象锁问题有疑问的,可以参见另一篇博文:http://www.cnblogs.com/kevin-yuan/archive/2013/04/27/3047511.html

启动多线程的两种情况比较

标签:style   blog   http   io   ar   color   使用   sp   for   

原文地址:http://www.cnblogs.com/kevin-yuan/p/4111242.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!