3

下面的代码发现输出结果不是顺序的,不是从小到大的,怎么保证是从小到大的顺序呢?

爪哇代码

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);  
        }  
    }  

}  
4

6 回答 6

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);  
        }  
    }  
}  
于 2012-08-28T04:00:48.867 回答
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++;.

于 2012-08-28T04:04:51.737 回答
1

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

拥有多个内核和/或超线程允许 CPU 有更多线程同时被赋予时间片。

但是,正如我所说,不能保证线程的时间片顺序以及每个单独的线程将在何时何地暂停和恢复。

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

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

于 2012-08-28T04:10:27.173 回答
0

我不是 Java 程序员,但同步锁不会阻止 getNum() 被乱序调用。而且因为 Thread1 在单独的线程上运行,调度程序可以自由地以任何顺序运行这 10 个线程。

同步确实保证了一次只能有一个线程执行同步部分内的代码。

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

于 2012-08-28T04:04:28.330 回答
0

实际上有2个问题。

1)。该Synchronization块特定于一个Thread1实例

你应该使用

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

或者

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

2)。核心问题是这样的。 Setting the static variable并且您 System.out.println()也应该同步,以便generationprinting按顺序排列。

于 2012-08-28T04:43:41.333 回答
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();
        }
    }
}
于 2013-02-06T02:30:15.487 回答