我一直以为,所有线程对锁的竞争是公平的,是随机的。我写了个小例子,结果让我不解。
按照下面的代码执行,结果确实是随机的,运行多次会得到不同的结果,这恰好说明了对锁的竞争是随机的。但是,若将第7行的注释去掉,让第7行变得可用,即每隔1秒创建一个新线程参与竞争,当5秒后锁被释放,这些线程的执行顺序竟然是固定的,程序的运行结果永远是:
Thread-0
Thread-3
Thread-2
Thread-1
是的,我运行了100遍,永远是这个结果。多线程的随机性荡然无存,这是为什么????
为什么创建线程前,稍微休眠一下,就会丧失线程的随机性??
求高手解释。
代码如下:
public class Test {

public static void main(String[] args) throws Exception{
ShareObj obj = new ShareObj(5);
new MyThread(obj).start();
for(int i=0; i<3; i++) {
// TimeUnit.SECONDS.sleep(1);
new MyThread(obj).start();
}
}
}class ShareObj {
private int sec;

public ShareObj(int sec) {
super();
this.sec = sec;
} public synchronized void doSomething() throws Exception {
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(sec);
sec = 0;
}
}class MyThread extends Thread { private ShareObj obj;

public MyThread(ShareObj obj) {
super();
this.obj = obj;
} @Override
public void run() {
try {
obj.doSomething();
} catch (Exception e) {
e.printStackTrace();
}
}
}多线程竞争

解决方案 »

  1.   

    每次运行均出现以下,(无论是否注释第七行)
    Thread-0
    Thread-3
    Thread-2
    Thread-1个人认为,打开第七行的意义是使得Thread.start()有足够时间得到CPU资源,每一个thread将会顺序得进入ContentionList.
    如果不打开第7行,Thread进入ContentionList的顺序有可能不一样。因为Thread.start()CPU有可能分配的CPU资源不一样。
    仅仅猜测,没有看过start,在JVM中的具体实现。你的这个帖子,和我发的帖子的问题非常相近。本人刚开始学习JAVA,关注中
    http://bbs.csdn.net/topics/390458017?page=1#post-394485275
      

  2.   

    没做过这方面的测试,个人觉得是不是没有休眠,线程中输出的内容太快,以至于通过IO向控制台打印时,控制台无法同一时刻打印多条记录,JVM在这一层做了一个线程池,并做了优先级处理。也就是说线程还是随机运行的,公平竞争的,只是在输出控制台这里被做了优先级排序。呵呵
    只是个人猜测!!!!!
      

  3.   

    LZ 看看这里
    《深入JVM锁机制1-synchronized 》
    http://blog.csdn.net/chen77716/article/details/6618779
      

  4.   

    情况比较复杂,楼主你注释时看到的随机效果,不是锁的随机效果,而是线程调取抢占CPU时间片的随机效果。而注释加上去后,线程必然按照固定顺序执行到 synchronized,此时才是真正体现锁的情况。锁的机制复杂得多,既要考虑公平性,又要考虑线程可执行条件。不过总体粗略来说,是队列结构。另外,楼主最好用 单CPU单线程的环境测试,否则情况还会更复杂。
      

  5.   

    Thread-0
    Thread-3
    Thread-1
    Thread-2
    请按任意键继续. . .这是我运行的结果,我是2核的机器。多次运行结果重复概率很大。不过我觉得 循环次数太少 如果你改成10,你得到的结果就很不同了。我猜测与JVM的机制有关,尽管说线程是公平竞争的。但是再怎么公平也有个“先来后到”的感觉,那么这个先来后到,我猜测是循环中运行start,但这个线程何时进入就绪状态 却是随机的。
      

  6.   

    你的解释我看懂了,这么解释很合理。
    我这个例子,锁的争夺就是按照栈的方式进行的,后进先出,严格有序,但这就没有公平性了,若不停地有新的线程来抢夺锁,那不是第一个进栈的线程永远也得不到锁了?
    你说的随机性【ldh911】已经解释的很清楚了,对于你的疑惑我简单的说一下
    1.非公平锁的效率比公平锁要高 这就是JVM中为什么使用的原因。
    2.后来的线程先获得锁这就是非公平的体现,不停的来新线程是一种合理的推测,不是一种合理的应用,并且操作系统对线程的数量也有大小规定。
    3.如果你的运用需要使用像公平锁一样的效果可以使用Lock下的FairSync类,另外闭锁,栅栏等也可以帮助你灵活的实现业务中的需求。
    4.饥饿死锁确实存在但一般是由你程序没有快速释放造成而并非JVM的原因。
      

  7.   

    前面有个童鞋的链接已经详细的说明的锁的内部实现机制,等待队列就是一个链表。
    每次插入和获取都在链表头操作,用CAS硬件指令实现,这样就不用调用操作系统锁来实现对链表的同步访问。
    这是一种权衡吧,虽然丧失了公平性,但提高了效率。