Java:线程顺序

时间:2012-08-28 03:47:27

标签: java multithreading

下面的代码发现输出结果不是顺序,不是从小到大,如何保证它是从小到大的顺序?

java代码

public class TestSync {  

    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        for (int i = 0; i < 10; i++) {  
            new Thread(new Thread1()).start();  
        }  

    }  

    public int getNum(int i) {  
        synchronized (this) {  
            i++;  
        }  
        return i;  
    }  

    static class Thread1 implements Runnable {  
        static Integer value = 0;  
        @Override  
        public void run() {  
            TestSync ts = new TestSync();  
            value = ts.getNum(value);  
            System.out.println("thread1:" + value);  
        }  
    }  

}  

6 个答案:

答案 0 :(得分:2)

虽然有人想知道为什么这是必要的,但这是一种方法。它并不优雅,但代表了对原始程序的微小改动:

import java.util.concurrent.*;

public class TestSync {

    public static void main(String[] args) {

    ExecutorService service = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
        service.submit(new Thread1());
    }

}

public int getNum(int i) {
    synchronized (this) {
        i++;
    }
    return i;
}

static class Thread1 implements Runnable {
    static Integer value = 0;
    @Override
    public void run() {
        TestSync ts = new TestSync();
        value = ts.getNum(value);
        System.out.println("thread1:" + value);
    }
}

}

这是一个更好的版本。它使用AtomicInteger作为计数器(在这种情况下可能是过度杀伤)来删除令人不快的getNum()方法:

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class TestSync {  
    static private AtomicInteger i = new AtomicInteger(0);

    public static void main(String[] args) {  
        ExecutorService service = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {  
            service.submit(new MyThread(i));  
        }  
        try { Thread.sleep(2*1000); } catch(Exception ex) {}
        service.shutdown();
    }  

    static class MyThread implements Runnable {  
        private int num = 0;
        public MyThread(int num) {
            this.num = num;
        }
        @Override  
        public void run() {  
            int value = i.incrementAndGet();
            System.out.println("thread # " + num + " value = " + value);  
        }  
    }  
}  

答案 1 :(得分:2)

你想要完成什么?您的代码仅同步对特定TestSync实例的调用。由于每个线程都创建自己的实例,因此就像你根本没有同步任何东西。您的代码没有做任何事情来同步或协调跨不同线程的访问。

我建议以下代码可能更符合您要完成的任务:

public static void main (String[] args) throws java.lang.Exception {
        for (int i = 0; i < 10; i++) {  
            new Thread1().start();  
        }  
}

//no need for this to be an instance method, or internally synchronized
public static int getNum(int i) {  
       return i + 1;  
}

static class Thread1 extends Thread {  
    static Integer value = 0;  

    @Override  
    public void run() {  
        while (value < 100) {
            synchronized(Thread1.class) {  //this ensures that all the threads use the same lock
                value = getNum(value);  
                System.out.println("Thread-" + this.getId() + ":  " + value);  
            }

            //for the sake of illustration, we sleep to ensure some other thread goes next
            try {Thread.sleep(100);} catch (Exception ignored) {} 
        }
    }  
}

此处的实例:http://ideone.com/BGUYY

请注意getNum()基本上是多余的。如果您使用简单value = getNum(value);替换value++;,则上述示例的工作方式相同。

答案 2 :(得分:1)

更不用说,无法保证在线程中运行时执行的订单代码是什么。这是由所谓的时间延迟引起的。 CPU将为特定线程分配“切片”时间。当切片启动时,它会有效地暂停线程,并允许其他线程获得切片。最终,它会回到暂停的线程并给它们额外的时间片,但这真的取决于CPU。

拥有多个内核和/或超线程允许CPU同时为更多线程提供时间片。

但是,正如我所说的那样,无法保证哪个顺序线程是时间点的,以及每个线程将在何时何地暂停和恢复。

这意味着“i ++”操作(以及相关打印)的完成顺序不一定是您启动线程的顺序。此外,线程之间的任何共享变量都应该使用“volatile”修饰符声明,以防止线程级别缓存该值。

如果你想强制进行顺序排序,你应该首先考虑为什么你首先使用线程而不是顺序循环。

答案 3 :(得分:0)

我不是Java程序员,但同步锁不会阻止无序调用getNum()。并且因为Thread1在不同的线程上运行,所以调度程序可以按任何顺序自由运行10个线程。

同步保证的是,一次只有一个线程可以在同步部分内执行代码。

如果您想要顺序执行,请在一个线程中调用所有getNums()。或者像ExecutorService一样使用线程队列。

答案 4 :(得分:0)

实际上有两个问题。

1)。 Synchronization块特定于Thread1实例

你应该使用

 synchronized(TestSync.class) {  
                //  
                //
            }

synchronized(Thread1.class) {  
                //  
                //
            }

2)。核心问题是。     Setting the static variableSystem.out.println()也应同步,以便generationprinting值依次排列。

答案 5 :(得分:0)

重写您的main()方法,如:

public static void main(String[] args)
{
    for (int i = 0; i < 10; i++)
    {
        Thread th = new Thread(new Thread1());
        th.start();
        try
        {
            th.join();
        }
        catch (InterruptedException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}