使用synchronized同步对象却有多个线程能同时访问,使用lock锁却达到目的了,不知道为什么求大神回答

zz939008303 2015-07-28 01:16:28
使用synchronized同步对象却有多个线程能同时访问,使用lock锁却达到目的了,不知道为什么求路过大神回答

package com.java.test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {

private volatile Integer count = 0;
private Lock lock = new ReentrantLock();

public Runnable run1 = new Runnable(){
@Override
public void run() {
// lock.lock();
synchronized(count) {
while(count < 1000) {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " run1: "+count++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// lock.unlock();
}};

public Runnable run2 = new Runnable(){
@Override
public void run() {
// lock.lock();
synchronized(count) {
while(count < 1000) {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " run2: "+count++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// lock.unlock();
}};

public Runnable run3 = new Runnable(){
@Override
public void run() {
// lock.lock();
synchronized(count) {
while(count < 1000) {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " run3: "+count++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// lock.unlock();
}};

public Runnable run4 = new Runnable(){
@Override
public void run() {
// lock.lock();
synchronized(count) {
while(count < 1000) {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " run4: "+count++);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// lock.unlock();
}};

public static void main(String[] args) throws InterruptedException {
LockTest t = new LockTest();
new Thread(t.run1).start();
Thread.sleep(1000);
new Thread(t.run2).start();
Thread.sleep(1000);
new Thread(t.run3).start();
Thread.sleep(1000);
new Thread(t.run4).start();
}

}

程序运行输出如下:
Thread-0 run1: 0
Thread-0 run1: 1
Thread-2 run3: 2
Thread-0 run1: 3
Thread-2 run3: 4
Thread-0 run1: 5
Thread-2 run3: 6
Thread-0 run1: 7
Thread-2 run3: 8
Thread-0 run1: 9
Thread-2 run3: 10
Thread-0 run1: 11
Thread-2 run3: 12
使用lock锁却达到了目的,不知道为什么...
Thread-0 run1: 0
Thread-0 run1: 1
Thread-0 run1: 2
Thread-0 run1: 3
Thread-0 run1: 4
Thread-0 run1: 5
Thread-0 run1: 6
Thread-0 run1: 7
...全文
1030 24 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
24 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhao_xinhu 2019-03-02
  • 打赏
  • 举报
回复
引用 7 楼 nicholasbobo 的回复:
引用 5 楼 nmyangym 的回复:
http://zhidao.baidu.com/link?url=kTHy33I23SLVfNJy30-MtDrepBkkquHEa9GFzY-oK1PkcW3ohBPSdU5L7eDYKKEFuEVlYYtzpjbL35C9yt_xHbSPbr6ELtjT-KK0s87eFXq
可能真是这样,int型变量的加减操作导致对象解锁了
引用 6 楼 nicholasbobo 的回复:
跑了几遍代码,我看出问题是在启动线程那里的sleep,现在分析如下: 1.启动线程时没有sleep,并且synchronized里的是一个对象的变量 如果启动线程之间没有sleep动作,那只会有一个线程会运行,不一定是哪个线程,几个一起抢锁,哪个抢到哪个运行,其它都被阻塞,直到运行的那个线程运行结束,其它就又开始争抢,抢到的开始运行,其它又阻塞,如此循环直到所有线程运行完。 2.启动线程时有sleep,并且synchronized里的是一个对象的变量 那线程是依次启动,不会同时争抢锁,启动的线程都会运行,并且synchronized里的变量都是同步的,即在某个线程里改变了值,另外的线程里值也是随着改变的。 3.synchronized里的是一个对象 假如把.synchronized里的改为LockTest.this,不管启动线程那里是同时启动,还是加了sleep依次启动,都只会有一个线程在运行,即抢到锁的那个,没有sleep时是随机的某个线程,有sleep的是第一启动的线程
你好,我也遇到了这个问题,关于你说的第一条为什么会出现这种情况啊
_jant 2017-01-22
  • 打赏
  • 举报
回复
锁的对象一定不能改变,你count++,锁都变了。 锁要保证唯一性
  • 打赏
  • 举报
回复
测试了下确实跟楼上几位说的是Integer自加的问题

public void run() {
//			lock.lock();
			synchronized(count) {
				while(count < 100) {
					try {
						
						System.out.println(Thread.currentThread().getName() + " run1: "+count++);
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
我将run方法中的sleep方法放到打印语句下后,即使你把main方法中的sleep方法去掉,同步块依然失效,大家可以试试。 至于楼主代码当main方法中的sleep方法去掉后可以实现同步,我猜想或许是cup时间片的问题
北国先生 2015-08-15
  • 打赏
  • 举报
回复
在对多个方法或代码块进行同步的时候,如果使用的是synchronized,那么所使用的锁一定要是同一个,否则很可能会因为获取不到锁造成线程永远得不到执行。还有,Thread的静态方法sleep是指定线程休眠一定的时间,在其休眠期间,是不会释放锁的,而Object的wait方法是挂起当前线程,直到被notify或notifyAll方法唤醒,在挂起的同时,会释放其持有的锁。上面这段代码的问题是:锁在某个线程执行的过程中被改变了。
readmemo 2015-08-15
  • 打赏
  • 举报
回复
你把synchronized同步块放到另外一个方法中去,可以实现和lock一样的效果。
  public void run1() {
    	 synchronized(count) {
             while(count < 1000) {
                 try {
                     Thread.sleep(2000);
                     System.out.println(Thread.currentThread().getName() + " run1: "+count++);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
         }
//         lock.unlock();
     
    }
结果和lock锁一样。 Thread-0 run1: 0 Thread-0 run1: 1 Thread-0 run1: 2 Thread-0 run1: 3 Thread-0 run1: 4 Thread-0 run1: 5 Thread-0 run1: 6 Thread-0 run1: 7 Thread-0 run1: 8 Thread-0 run1: 9
ArayChou 2015-08-05
  • 打赏
  • 举报
回复
引用 12 楼 Chimerica_Computer 的回复:
Java中synchronized同步的对象不能是Integer等类型,因为是Java的自动封箱和解箱操作在作怪。这里的i++实际上是i = new Integer(i+1),所以执行完i++后,i已经不是原来的对象了,同步块自然就无效了。
正解,学习,我专门写了段代码来调试看,i++后,i这个对象指向了新对象。
zhaoqiubo 2015-08-05
  • 打赏
  • 举报
回复
sleep是不会释放锁的。在有sleep的时候,事实上线程二所得到的锁,不是线程一所拥有的锁。
zhaoqiubo 2015-08-05
  • 打赏
  • 举报
回复
先不考虑你用互斥锁的时候,因为互斥锁是没有变化的,在整个程序运行过程中。

而你 的syn同步锁,加sleep和不加sleep情况如下:
在没有sleep的情况下。四个线程都要去争取锁。争取哪个呢?争取count=0这个锁。
但是第一个线程先运行,所以第一个线程得到了锁,一直运行完毕。其他几个线程一直在等count=0的锁释放。等第一个线程释放之后,count已经等于9了。所以他们不会执行。
如果有sleep的情况下,第一个线程得到了count=0的锁,但是sleep之后,启动第二个线程的时候,第二个线程去争取锁,这个时候count已经不等于0了,count等于另外一个,这时第二个线程也取到了锁,只不过不是count=0,可能是count=?(根据你自己定义sleep时间而定),以此类推。

这个说法可以验证的,不信你把main函数中的时间调的过短,或者过长,线程2,都是取不到锁的。

另外,你这个锁是无效的,一般定义锁都会定义成final对象,不会定义一个变得对象作为锁。锁变了就没锁的意义了。
zhaoqiubo 2015-08-04
  • 打赏
  • 举报
回复
count是类LockTest的成员变量,这个类只被实例化了一次,count必然要跟着循环发生变化,这个跟锁没关系,你锁不锁他都得变。现在需要考虑的是,为什么main方法里面加上了sleep运行的结果会不一样。打开lock跟synchronize运行加过又不一样。我中午出去吃饭,下午回来看看。
zhaoqiubo 2015-08-04
  • 打赏
  • 举报
回复
引用 12 楼 Chimerica_Computer 的回复:
Java中synchronized同步的对象不能是Integer等类型,因为是Java的自动封箱和解箱操作在作怪。这里的i++实际上是i = new Integer(i+1),所以执行完i++后,i已经不是原来的对象了,同步块自然就无效了。
为什么去掉sleep就仍然只执行线程1呢?你这个说法不能解释。
zhaoqiubo 2015-08-04
  • 打赏
  • 举报
回复
你的count发生变化,原来的锁就被释放了。你可以随便定义一个Object obj代替count,这样syn和Lock的结果就一样了。
zhangdekun 2015-07-30
  • 打赏
  • 举报
回复
count不是同一个对象了
Chimerica_Computer 2015-07-30
  • 打赏
  • 举报
回复
Java中synchronized同步的对象不能是Integer等类型,因为是Java的自动封箱和解箱操作在作怪。这里的i++实际上是i = new Integer(i+1),所以执行完i++后,i已经不是原来的对象了,同步块自然就无效了。
  • 打赏
  • 举报
回复
引用 10 楼 homestay_anniversary 的回复:
我试了下,把main方法中Thread.sleep注掉,就好使了。。这是个什么鬼
好诡异,可以知道的是在count++的操作中,线程不知怎么释放了锁,导致两个线程交替运行。main中的Thread.sleep按理只是让线程二稍晚一些运行,如果线程1会在count++中释放锁,那么无论有没有这个sleep方法都应该看得到线程间的切换,然而,实际是,去掉这个sleep就可以实现锁功能,我的电脑上是这样。 另外,在有THread.sleep的情况下,把线程2的run方法改为一个while空的无穷循环,如果像上面那样交替运行,那么一旦得到count锁,线程2将无穷循环,永远不放锁,导致Thread1不会被执行。然而测试中,发现这次线程2永远得不到锁,看上去线程1又可以实现它的同步块功能了。。感觉这像个线程管理器的小bug,根本就不科学
  • 打赏
  • 举报
回复
我试了下,把main方法中Thread.sleep注掉,就好使了。。这是个什么鬼
nicholasbobo 2015-07-28
  • 打赏
  • 举报
回复
引用 7 楼 nicholasbobo 的回复:
引用 5 楼 nmyangym 的回复:
http://zhidao.baidu.com/link?url=kTHy33I23SLVfNJy30-MtDrepBkkquHEa9GFzY-oK1PkcW3ohBPSdU5L7eDYKKEFuEVlYYtzpjbL35C9yt_xHbSPbr6ELtjT-KK0s87eFXq
可能真是这样,int型变量的加减操作导致对象解锁了
,试了下,的确是,如果不对count做加法的操作,永远都会只在第一个线程里运行
nicholasbobo 2015-07-28
  • 打赏
  • 举报
回复
引用 5 楼 nmyangym 的回复:
http://zhidao.baidu.com/link?url=kTHy33I23SLVfNJy30-MtDrepBkkquHEa9GFzY-oK1PkcW3ohBPSdU5L7eDYKKEFuEVlYYtzpjbL35C9yt_xHbSPbr6ELtjT-KK0s87eFXq
可能真是这样,int型变量的加减操作导致对象解锁了
nicholasbobo 2015-07-28
  • 打赏
  • 举报
回复
跑了几遍代码,我看出问题是在启动线程那里的sleep,现在分析如下: 1.启动线程时没有sleep,并且synchronized里的是一个对象的变量 如果启动线程之间没有sleep动作,那只会有一个线程会运行,不一定是哪个线程,几个一起抢锁,哪个抢到哪个运行,其它都被阻塞,直到运行的那个线程运行结束,其它就又开始争抢,抢到的开始运行,其它又阻塞,如此循环直到所有线程运行完。 2.启动线程时有sleep,并且synchronized里的是一个对象的变量 那线程是依次启动,不会同时争抢锁,启动的线程都会运行,并且synchronized里的变量都是同步的,即在某个线程里改变了值,另外的线程里值也是随着改变的。 3.synchronized里的是一个对象 假如把.synchronized里的改为LockTest.this,不管启动线程那里是同时启动,还是加了sleep依次启动,都只会有一个线程在运行,即抢到锁的那个,没有sleep时是随机的某个线程,有sleep的是第一启动的线程
nmyangym 2015-07-28
  • 打赏
  • 举报
回复
http://zhidao.baidu.com/link?url=kTHy33I23SLVfNJy30-MtDrepBkkquHEa9GFzY-oK1PkcW3ohBPSdU5L7eDYKKEFuEVlYYtzpjbL35C9yt_xHbSPbr6ELtjT-KK0s87eFXq
晴天_ccc 2015-07-28
  • 打赏
  • 举报
回复
纠正一下上面两位,sleep()导致线程的休眠是不会释放锁的,只有wait()才会释放锁。楼主的代码中如果锁使用定义额外一个对象private Object obj=new Object();作为锁,同样可以达到楼主需要的效果,但是使用count不行,这里我也觉得奇怪。
加载更多回复(3)

62,634

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧