java多线程中,锁不起作用?

jlty258 2016-05-03 11:50:27
package com.multi;

public class ThreadTest implements Runnable {
private Integer ticket = 10;
private Integer count = 0;
private byte[] lock = new byte[0];

public void run() {
synchronized (ticket) {
while (true) {
if (ticket <= -100) {
break;
}
System.out.println(String.format("thread: %s , tiketnum = %d do some thing"
,Thread.currentThread().getName()
,ticket--));
count++;
}
}

}

public static void main(String[] args) {
ThreadTest mTh1 = new ThreadTest();
Thread[] th = new Thread[5];
for (int i = 0; i < 5; i++) {
th[i]= new Thread(mTh1,"th"+i);
th[i].start();
}
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(mTh1.count);
}
}

执行结果
thread: th4 , tiketnum = 6 do some thing
thread: th3 , tiketnum = 7 do some thing
thread: th4 , tiketnum = 5 do some thing
thread: th3 , tiketnum = 4 do some thing
thread: th3 , tiketnum = 2 do some thing
thread: th4 , tiketnum = 3 do some thing
thread: th4 , tiketnum = 0 do some thing
thread: th4 , tiketnum = -1 do some thing
thread: th4 , tiketnum = -2 do some thing
thread: th4 , tiketnum = -3 do some thing
thread: th4 , tiketnum = -4 do some thing
thread: th4 , tiketnum = -5 do some thing
thread: th4 , tiketnum = -6 do some thing
thread: th4 , tiketnum = -7 do some thing
thread: th4 , tiketnum = -8 do some thing
thread: th1 , tiketnum = 9 do some thing
……………………

…………
...全文
696 29 打赏 收藏 转发到动态 举报
写回复
用AI写文章
29 条回复
切换为时间正序
请发表友善的回复…
发表回复
jlty258 2016-05-10
  • 打赏
  • 举报
回复
引用 27 楼 skgary 的回复:
[quote=引用 22 楼 jlty258 的回复:] [quote=引用 21楼键圣 的回复:]private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
这个解释通透,多谢大神。[/quote] 对于一般性锁的解释,21楼是正确的。 但对于你这段代码,相反的,我不认为21楼的解释完全正确。 其实最关键的在于ticket-- 这一句话的理解 简单来说,ticket--这句话,已经让ticket这个变量指向了一个新的Integer对像,所以你才会锁不住。 但今天特地去查了一下,其实答案非常非常的有意思 这个和integer对像的autoboxing 和unboxing这个功能有关系 https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html 简单来说,ticket-- 这句话,等同于 ticket = new Integer(ticket.intValue-1)。而不是什么ticket.decrease()这样的一个函数。 所以,如果你只想搞清楚对像锁是怎么回事,那么就用一般的object对象,而不是integer或float之类有autoboxing 和unboxing功能的对象。 [/quote] 这样来看,包装类对象从设计上就不合适用作锁。 有一点需要更正下,如果同步语句块中代码是 System.out.println(String.format("thread: %s , tiketnum = %d do some thing",Thread.currentThread().getName(),ticket)); ticket--; 那么执行结果与线程数目有关,当线程数量较少时,数十个或者更少,那么无论测试多少遍,执行结果一直是单线程的; 当线程数目上升到数百个左右时,执行结果就是乱序执行了。 出现这种现象的原因应该是什么,资源竞争激烈影响了底层的线程调度?
skgary 2016-05-10
  • 打赏
  • 举报
回复
引用 28 楼 jlty258 的回复:
这样来看,包装类对象从设计上就不合适用作锁。 有一点需要更正下,如果同步语句块中代码是 System.out.println(String.format("thread: %s , tiketnum = %d do some thing",Thread.currentThread().getName(),ticket)); ticket--; 那么执行结果与线程数目有关,当线程数量较少时,数十个或者更少,那么无论测试多少遍,执行结果一直是单线程的; 当线程数目上升到数百个左右时,执行结果就是乱序执行了。 出现这种现象的原因应该是什么,资源竞争激烈影响了底层的线程调度?
取决于你后面线程执行到 synchronized (ticket)时,别的线程有没有执行过 ticket--;这一句。 一旦有一个线程执行到了ticket--,那么这之后执行到synchronized 都锁不住。
skgary 2016-05-09
  • 打赏
  • 举报
回复
引用 22 楼 jlty258 的回复:
[quote=引用 21楼键圣 的回复:]private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
这个解释通透,多谢大神。[/quote] 对于一般性锁的解释,21楼是正确的。 但对于你这段代码,相反的,我不认为21楼的解释完全正确。 其实最关键的在于ticket-- 这一句话的理解 简单来说,ticket--这句话,已经让ticket这个变量指向了一个新的Integer对像,所以你才会锁不住。 但今天特地去查了一下,其实答案非常非常的有意思 这个和integer对像的autoboxing 和unboxing这个功能有关系 https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html 简单来说,ticket-- 这句话,等同于 ticket = new Integer(ticket.intValue-1)。而不是什么ticket.decrease()这样的一个函数。 所以,如果你只想搞清楚对像锁是怎么回事,那么就用一般的object对象,而不是integer或float之类有autoboxing 和unboxing功能的对象。
  • 打赏
  • 举报
回复
引用 25 楼 jiaotuwoaini 的回复:
[quote=引用 23 楼 mymy1026 的回复:] [quote=引用 21 楼 jiaotuwoaini 的回复:] private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
第三天来看这个帖子了。如果是这种情况,那为什么ticker--,拿到外边又顺序执行了呢?[/quote] 拿到哪里?外面指的是哪行?[/quote] 看八楼
jlty258 2016-05-06
  • 打赏
  • 举报
回复
引用 19楼skgary 的回复:
[quote=引用 15 楼 jlty258 的回复:] [quote=引用 12楼skgary 的回复:]的确,从多线程机制上,很让人费解。 个人认为,jvm优化导致的问题。 Integer虽然是个对像,但不纯粹,当进行--操作以后,这个对像已经变化过了。 也就是说,第二个线程启动时,integer已经不是原来那个对像了。 两种写法不一样,原因是速度不一样,比的是启动速度和打印速度谁先谁快。
如果假定--操作之后,ticket对象确实变化了。那么,在--操作之后,锁已经相当于被释放了。执行结果应该是多线程乱序执行。这没法解释第二种写法[/quote] ==》两种写法不一样,原因是速度不一样,比的是启动速度和打印速度谁先谁快。 第一种写法,先执行--操作,这样肯定比启动第二个线程快。 第二种写法,先打印,这样就有可能给启动第二个线程机会了。 不信,你可以改改--操作的位置 这是我觉得唯一可能的解释了。 如果是一般的对像的话,是肯定可以锁住的。[/quote]还真是,将--操作移到println前面,输出又是乱序执行了。不过你说的一般对象和integer不是纯粹的对象指的是?
skgary 2016-05-06
  • 打赏
  • 举报
回复
引用 15 楼 jlty258 的回复:
[quote=引用 12楼skgary 的回复:]的确,从多线程机制上,很让人费解。 个人认为,jvm优化导致的问题。 Integer虽然是个对像,但不纯粹,当进行--操作以后,这个对像已经变化过了。 也就是说,第二个线程启动时,integer已经不是原来那个对像了。 两种写法不一样,原因是速度不一样,比的是启动速度和打印速度谁先谁快。
如果假定--操作之后,ticket对象确实变化了。那么,在--操作之后,锁已经相当于被释放了。执行结果应该是多线程乱序执行。这没法解释第二种写法[/quote] ==》两种写法不一样,原因是速度不一样,比的是启动速度和打印速度谁先谁快。 第一种写法,先执行--操作,这样肯定比启动第二个线程快。 第二种写法,先打印,这样就有可能给启动第二个线程机会了。 不信,你可以改改--操作的位置 这是我觉得唯一可能的解释了。 如果是一般的对像的话,是肯定可以锁住的。
键圣 2016-05-06
  • 打赏
  • 举报
回复
引用 23 楼 mymy1026 的回复:
[quote=引用 21 楼 jiaotuwoaini 的回复:] private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
第三天来看这个帖子了。如果是这种情况,那为什么ticker--,拿到外边又顺序执行了呢?[/quote] 拿到哪里?外面指的是哪行?
  • 打赏
  • 举报
回复
引用 21 楼 jiaotuwoaini 的回复:
private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
第三天来看这个帖子了。如果是这种情况,那为什么ticker--,拿到外边又顺序执行了呢?
  • 打赏
  • 举报
回复
引用 23 楼 mymy1026 的回复:
[quote=引用 21 楼 jiaotuwoaini 的回复:] private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
第三天来看这个帖子了。如果是这种情况,那为什么ticker--,拿到外边又顺序执行了呢?[/quote] 说话不严谨,是单线程执行了呢
jlty258 2016-05-06
  • 打赏
  • 举报
回复
引用 21楼键圣 的回复:
private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
这个解释通透,多谢大神。
键圣 2016-05-06
  • 打赏
  • 举报
回复
private Integer ticket = 10; 一个对象一把锁。 基础类型对象是存放在Java虚拟机栈中的,你每次操作改变ticket的时候相当于重新在栈中创建了对象,你拿到的锁是当前对象的锁,所以说你申请的不是同一个对象的锁。而引用对象会在栈里面存放一个引用,实际对象存放在堆空间里面,变量名相当于一个引用,你引用不变那么指向的就是同一份堆内存空间,也就是同一个对象,所以synchronized的时候不能是简单类型的对象。
小灰狼 2016-05-05
  • 打赏
  • 举报
回复
ticket 已经被你重新赋值了,锁的已经不同一个东西了 比如会议室是一个同步资源,谁想进去开会,就是独占的 但是楼主的程序里,ticket 一直被重复赋值,也就是说,你的会议地点时刻都在变,一件事没有讨论完,就跑到另外一个会议室了
jlty258 2016-05-05
  • 打赏
  • 举报
回复
引用 16楼小灰狼 的回复:
ticket 已经被你重新赋值了,锁的已经不同一个东西了 比如会议室是一个同步资源,谁想进去开会,就是独占的 但是楼主的程序里,ticket 一直被重复赋值,也就是说,你的会议地点时刻都在变,一件事没有讨论完,就跑到另外一个会议室了
并不是这个原因。第二种写法就解释不通
jlty258 2016-05-04
  • 打赏
  • 举报
回复
引用 12楼skgary 的回复:
的确,从多线程机制上,很让人费解。 个人认为,jvm优化导致的问题。 Integer虽然是个对像,但不纯粹,当进行--操作以后,这个对像已经变化过了。 也就是说,第二个线程启动时,integer已经不是原来那个对像了。 两种写法不一样,原因是速度不一样,比的是启动速度和打印速度谁先谁快。
如果假定--操作之后,ticket对象确实变化了。那么,在--操作之后,锁已经相当于被释放了。执行结果应该是多线程乱序执行。这没法解释第二种写法
jlty258 2016-05-04
  • 打赏
  • 举报
回复
引用 12楼skgary 的回复:
的确,从多线程机制上,很让人费解。 个人认为,jvm优化导致的问题。 Integer虽然是个对像,但不纯粹,当进行--操作以后,这个对像已经变化过了。 也就是说,第二个线程启动时,integer已经不是原来那个对像了。 两种写法不一样,原因是速度不一样,比的是启动速度和打印速度谁先谁快。
如果假定ticket的值发生变化,这个ticket对象也
skgary 2016-05-04
  • 打赏
  • 举报
回复
的确,从多线程机制上,很让人费解。 个人认为,jvm优化导致的问题。 Integer虽然是个对像,但不纯粹,当进行--操作以后,这个对像已经变化过了。 也就是说,第二个线程启动时,integer已经不是原来那个对像了。 两种写法不一样,原因是速度不一样,比的是启动速度和打印速度谁先谁快。
xiaovhao 2016-05-04
  • 打赏
  • 举报
回复
ticker锁不停的在变化,存在说当String.format这个执行完,但是还没有执行system.out.println,这个时候ticker的值已经变了,也就是说其他线程也可以进来了
bajinggong 2016-05-04
  • 打赏
  • 举报
回复
我把你的打印换成System.out.println(Thread.currentThread().getName());好像就不对
jlty258 2016-05-04
  • 打赏
  • 举报
回复
引用 9楼森雾-迷林 的回复:
[quote=引用 8 楼 jlty258 的回复:] 又测试了一下,发现关键在于 System.out.println(String.format("thread: %s , tiketnum = %d do some thing",Thread.currentThread().getName(),ticket--)); 如果将ticket--移出来,改写成 System.out.println(String.format("thread: %s , tiketnum = %d do some thing",Thread.currentThread().getName(),ticket)); ticket--; 输出就正常了 thread: th0 , tiketnum = 10 do some thing thread: th0 , tiketnum = 9 do some thing thread: th0 , tiketnum = 8 do some thing thread: th0 , tiketnum = 7 do some thing thread: th0 , tiketnum = 6 do some thing thread: th0 , tiketnum = 5 do some thing thread: th0 , tiketnum = 4 do some thing thread: th0 , tiketnum = 3 do some thing thread: th0 , tiketnum = 2 do some thing thread: th0 , tiketnum = 1 do some thing thread: th0 , tiketnum = 0 do some thing thread: th0 , tiketnum = -1 do some thing …………………… ………………
66666666666 [/quote]搞不懂,为啥一移出来就输出正常?
迷林 2016-05-04
  • 打赏
  • 举报
回复
引用 8 楼 jlty258 的回复:
又测试了一下,发现关键在于 System.out.println(String.format("thread: %s , tiketnum = %d do some thing",Thread.currentThread().getName(),ticket--)); 如果将ticket--移出来,改写成 System.out.println(String.format("thread: %s , tiketnum = %d do some thing",Thread.currentThread().getName(),ticket)); ticket--; 输出就正常了 thread: th0 , tiketnum = 10 do some thing thread: th0 , tiketnum = 9 do some thing thread: th0 , tiketnum = 8 do some thing thread: th0 , tiketnum = 7 do some thing thread: th0 , tiketnum = 6 do some thing thread: th0 , tiketnum = 5 do some thing thread: th0 , tiketnum = 4 do some thing thread: th0 , tiketnum = 3 do some thing thread: th0 , tiketnum = 2 do some thing thread: th0 , tiketnum = 1 do some thing thread: th0 , tiketnum = 0 do some thing thread: th0 , tiketnum = -1 do some thing …………………… ………………
66666666666
加载更多回复(8)

62,615

社区成员

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

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