JAVA多线程安全问题

sinat_35134692 2017-02-26 04:41:58
class Tickets implements Runnable
{
private int num=100;
public void run()
{
show();
}
public void show()
{
while(num>0)
{

try{
Thread.sleep(10);
}
catch(InterruptedException e)
{}
num--;
System.out.println("This is"+Thread.currentThread().getName()+"window,there are "+num+"tickets");
}

}
}
public class Safety_Problem_Of_Thread {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Tickets T=new Tickets();// TODO code application logic here
Thread t1=new Thread(T);
Thread t2=new Thread(T);
t1.start();
t2.start();

}

}



——————————————————————————————————
结果:
This isThread-1window,there are 99tickets
This isThread-0window,there are 99tickets
This isThread-0window,there are 98tickets
This isThread-1window,there are 98tickets
This isThread-1window,there are 96tickets
This isThread-0window,there are 96tickets
This isThread-0window,there are 95tickets
This isThread-1window,there are 95tickets
This isThread-1window,there are 94tickets
This isThread-0window,there are 94tickets
This isThread-1window,there are 93tickets
This isThread-0window,there are 92tickets


结果只列出了一部分。我知道这段代码存在安全问题,并没有设置同步。但是为什么结果中会输出两个99,98,96,95........这是什么原因呢,可否分析一下???谢谢
...全文
624 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
wgd81685 2017-02-27
  • 打赏
  • 举报
回复
引用 11 楼 sinat_35134692 的回复:
[quote=引用 10 楼 wgd81685的回复:]问题出在num--这句代码 线程一运行到num--时,运算并未完成,此时num仍为100 线程二也运行到num--,此时num仍然为100,开始运算后num为99 然后线程一开运算,100减1仍然为99 对num--这句代码加锁,就不会出现上述情况了
可以理解为是多线程并发,然后线程工作内存中各有一份num的拷贝副本,然后同时发生了副本的修改和输出,但是还没有对堆内存中的num进行覆盖么?[/quote] 这个地方是java自减的内存处理,具体的我也不太懂,一起百度一下
sinat_35134692 2017-02-27
  • 打赏
  • 举报
回复
引用 10 楼 wgd81685的回复:
问题出在num--这句代码 线程一运行到num--时,运算并未完成,此时num仍为100 线程二也运行到num--,此时num仍然为100,开始运算后num为99 然后线程一开运算,100减1仍然为99 对num--这句代码加锁,就不会出现上述情况了
可以理解为是多线程并发,然后线程工作内存中各有一份num的拷贝副本,然后同时发生了副本的修改和输出,但是还没有对堆内存中的num进行覆盖么?
wgd81685 2017-02-27
  • 打赏
  • 举报
回复
问题出在num--这句代码 线程一运行到num--时,运算并未完成,此时num仍为100 线程二也运行到num--,此时num仍然为100,开始运算后num为99 然后线程一开运算,100减1仍然为99 对num--这句代码加锁,就不会出现上述情况了
qq_37220796 2017-02-27
  • 打赏
  • 举报
回复
引用 6 楼 sinat_35134692的回复:
[quote=引用 5 楼 qq_37220796的回复:]线程1进去循环,num等于100,开始sleep,此时num并没有自减。 线程2进去循环,num也等于100这时候你不管怎么自减都无所谓了,因为是不同的线程。 我也刚学完线程,不知道理解的对不对,把它上个锁就解决了
num在内存中只有一份对吧,因为只有一个对象封装了任务,就是多个线程执行一个任务。而且也不存多个线程在同时执行一条指令对同一个数据进行操作。但是这个输出的结果给人感觉好像是有两份num在被同时操作[/quote] num又不是static
X元素 2017-02-27
  • 打赏
  • 举报
回复
建议楼主看看多线程内存模型,然后考虑一下你线程中的num变量,
mxiaolaohu 2017-02-27
  • 打赏
  • 举报
回复
引用 4 楼 sinat_35134692的回复:
[quote=引用 3 楼 mxiaolaohu 的回复:] 这个问题,我想我能给你解答。其实很简单,程序每运行完一个线程的一条指令,都会让每个线程去抢下一次执行语句的权力,谁抢到谁执行。你这个程序中有两个线程,假如a抢到了此时num=50执行完后,系统会把执行权释放出去。假如这一次是b抢到了,此时num是49还是50呢?答案是50因为a还没来得及将num减一,就把下次执行的权力让了出去。所以就引出了同步代码块,同步方法和同步锁
是呀,我明白,但是如果按照你说的,num=50a的CPU时间执行完了切换成b,那么b这时候应该执行num--,成为49才能输出呀,但是程序结果出现了两个50。如果说b已经执行完num--,此时num=50,并且等待执行输出时,被切换到a,那a也应该执行num--再输出啊,这样就不会出现2个50的输出。总不能两个线程同时执行了num--吧?[/quote] 你应该把num放在打印方法的下面,把睡眠放中间,多运行几次。结果每次都不一样。你也可以debug下,就知道他怎么运行的了。
sinat_35134692 2017-02-27
  • 打赏
  • 举报
回复
引用 14 楼 zs808的回复:
多线程的资源访问解决方案,就是如何将非原子操作通过同步块或者锁变为原子操作。 例如上述例子,只需要将对num这个共享资源的非原子操作加上synchronized块转换成原子操作即可,代码:

while (true) {

				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
				}
				synchronized (this) {  //将num的费原子操作包装成原子操作
					if(num>0){//包装while(num>0)中的num>0这个非原子操作
						num--;//包装num--非原子操作
						System.out
						.println("This is" + Thread.currentThread().getName() + "window,there are " + num + "tickets"); //包装+num+非原子操作
					}else{
						break;
					}
				}
				
			}
非常感谢详细回答,有点儿明白了
zs808 2017-02-27
  • 打赏
  • 举报
回复
多线程的资源访问解决方案,就是如何将非原子操作通过同步块或者锁变为原子操作。 例如上述例子,只需要将对num这个共享资源的非原子操作加上synchronized块转换成原子操作即可,代码:

while (true) {

				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
				}
				synchronized (this) {  //将num的费原子操作包装成原子操作
					if(num>0){//包装while(num>0)中的num>0这个非原子操作
						num--;//包装num--非原子操作
						System.out
						.println("This is" + Thread.currentThread().getName() + "window,there are " + num + "tickets"); //包装+num+非原子操作
					}else{
						break;
					}
				}
				
			}
zs808 2017-02-27
  • 打赏
  • 举报
回复
这个问题主要是出现在num--这个非原子操作上。 num--其实是分为三步,第一步读取全局变量num的值到栈中,第二步将num出栈,执行加法运算,第三部,将运算结果存储到全局变量num中。其中单个步骤的操作是原子的,但是多个步骤加起来就不是原子操作了。你可以设想一下:如果线程A在线程B执行第三步之前执行了第一步,这时候num的相同值就会同时存在于A与B中,然后A与B再执行加法运算,写入,最终的值肯定是要少加一次的。这也就是为什么最终结果要小于100的原因。
sinat_35134692 2017-02-26
  • 打赏
  • 举报
回复
引用 5 楼 qq_37220796的回复:
线程1进去循环,num等于100,开始sleep,此时num并没有自减。 线程2进去循环,num也等于100这时候你不管怎么自减都无所谓了,因为是不同的线程。 我也刚学完线程,不知道理解的对不对,把它上个锁就解决了
num在内存中只有一份对吧,因为只有一个对象封装了任务,就是多个线程执行一个任务。而且也不存多个线程在同时执行一条指令对同一个数据进行操作。但是这个输出的结果给人感觉好像是有两份num在被同时操作
qq_37220796 2017-02-26
  • 打赏
  • 举报
回复
线程1进去循环,num等于100,开始sleep,此时num并没有自减。 线程2进去循环,num也等于100这时候你不管怎么自减都无所谓了,因为是不同的线程。 我也刚学完线程,不知道理解的对不对,把它上个锁就解决了
sinat_35134692 2017-02-26
  • 打赏
  • 举报
回复
引用 3 楼 mxiaolaohu 的回复:
这个问题,我想我能给你解答。其实很简单,程序每运行完一个线程的一条指令,都会让每个线程去抢下一次执行语句的权力,谁抢到谁执行。你这个程序中有两个线程,假如a抢到了此时num=50执行完后,系统会把执行权释放出去。假如这一次是b抢到了,此时num是49还是50呢?答案是50因为a还没来得及将num减一,就把下次执行的权力让了出去。所以就引出了同步代码块,同步方法和同步锁
是呀,我明白,但是如果按照你说的,num=50a的CPU时间执行完了切换成b,那么b这时候应该执行num--,成为49才能输出呀,但是程序结果出现了两个50。如果说b已经执行完num--,此时num=50,并且等待执行输出时,被切换到a,那a也应该执行num--再输出啊,这样就不会出现2个50的输出。总不能两个线程同时执行了num--吧?
mxiaolaohu 2017-02-26
  • 打赏
  • 举报
回复
这个问题,我想我能给你解答。其实很简单,程序每运行完一个线程的一条指令,都会让每个线程去抢下一次执行语句的权力,谁抢到谁执行。你这个程序中有两个线程,假如a抢到了此时num=50执行完后,系统会把执行权释放出去。假如这一次是b抢到了,此时num是49还是50呢?答案是50因为a还没来得及将num减一,就把下次执行的权力让了出去。所以就引出了同步代码块,同步方法和同步锁
曾阿牛_ 2017-02-26
  • 打赏
  • 举报
回复
不太清楚,顶一下
sinat_35134692 2017-02-26
  • 打赏
  • 举报
回复
是和处理器有关么,双核四线程的处理器意味着可以同时处理4个线程?那就是说线程0和线程1是同时执行的么?还是不理解啊,即时是同时执行不是也是两个线程共同完成一个任务么,num变量应该只有一份啊,为什么会经过两个“--”后怎么还是输出同一个结果

62,614

社区成员

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

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