首页 > 代码库 > 启动多线程的两种情况比较

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

启动多线程有两种方式:(都是在主线程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

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