不太理解线程不安全产生的原因

longzaierlian 2016-08-26 05:37:16
package com.rupeng;

public class ThreadDemo2
{

public static void main(String[] args)
{
TicketWin win=new TicketWin();

Thread win1=new Thread(win);
Thread win2=new Thread(win);
Thread win3=new Thread(win);
Thread win4=new Thread(win);

win1.start();
win2.start();
win3.start();
win4.start();
//自己测试发现,用一个继承了Thread类的子类作为参数,传递给
//new Thread()方法时,创建的线程还是原来那个,线程没有增加
//就像是把一个线程重复打开四次。



}

}

class TicketWin implements Runnable
{
private int ticketNumber=10;

public void run()
{
while(ticketNumber>0)
{

try {
Thread.sleep(1000);
} catch (InterruptedException e) {

e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--"+ticketNumber);
ticketNumber--;
}
}
}


上面的代码运行后,其中的一次结果是这样的:



我的问题是:
这四个线程是怎么抢占CPU的?最后才产生了这样一个结果(都是2)?
...全文
1164 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
  • 打赏
  • 举报
回复
引用 11 楼 NewMoons 的回复:
这篇文章的题目虽然和你的问题不相关,但仔细看完必有裨益。 http://www.cnblogs.com/dolphin0520/p/3920373.html#!comments
哇,大佬。太关键了。看了这篇文章,对于多线程的理解简直是天翻地覆!!!
  • 打赏
  • 举报
回复
你们都忽略一个问题就是最后一次输出的三个减号其实有个是负号,就是最后一次输出的是-2, 你要求的是只有大于0才会进循环,这就和你当初写RUN方法的时候想法产生了偏差。 原因就是线程不同步,四个线程最后一次拿到ticketNum确实是大于0的才进while循环,由于你不是同步的,每个线程都依次对ticketNum操作了一次,导致最后出现了负数。 加锁实现同步就避免了这个问题,只有当一个线程一次循环结束另一个线程才能拿到ticketNum判断再进循环,这样就是 10-1依次输出
qq_18864933 2016-08-29
  • 打赏
  • 举报
回复
引用 13 楼 qq_18864933 的回复:
回答一下。 方法中的局部变量本身不存在现成安全问题 我们可以吧win 引用的实例当成单例 但是操作的变量 的值还是存在每个线程的内存中而不是主存
。。 说错了 你那个就是成员
qq_18864933 2016-08-29
  • 打赏
  • 举报
回复
你这个现象和 ThredLocal是一样的
qq_18864933 2016-08-29
  • 打赏
  • 举报
回复
回答一下。 方法中的局部变量本身不存在现成安全问题 我们可以吧win 引用的实例当成单例 但是操作的变量 的值还是存在每个线程的内存中而不是主存
  • 打赏
  • 举报
回复
甲和乙同时发现了地上的100块钱,都想捡起来放自己腰包,没有一个管控机制的话,就会打架。
NewMoons 2016-08-28
  • 打赏
  • 举报
回复
这篇文章的题目虽然和你的问题不相关,但仔细看完必有裨益。 http://www.cnblogs.com/dolphin0520/p/3920373.html#!comments
rickylin86 2016-08-27
  • 打赏
  • 举报
回复
引用 5 楼 longzaierlian 的回复:
[quote=引用 3 楼 ddddouche 的回复:] 比如操作数据库,一个人在读取数据的时候,另外一个人在窜改你正在读的数据。这样安全吗?你所贴的代码里面,每个ticketNum都是每条线程都是自己的,这样肯定是线程安全的。可以试着把ticketNum改成全局变量, 然后用几条线程对它进行操作就知道为什么需要线程间数据同步了。
我用一个类对象,创建了四个线程, TicketWin win=new TicketWin(); Thread win1=new Thread(win); Thread win2=new Thread(win); Thread win3=new Thread(win); Thread win4=new Thread(win); 这四个线程操作的ticketNum是唯一的,这个就是共享的资源了。只不过这四个线程对ticketNum进行的操作是一样的,都是"--"。 我想知道的问题是这四个线程是如何轮流对共享变量ticketNum进行减减操作的。 这个例子已经体现了线程不安全的问题,因为如果线程是安全的,那么结果就会使顺序的9 8 7 6 5 4 ……了,而不会出现四个线程打印相同的值的情况了。[/quote] 之前看错了。确实是操作同一个变量. 因为本身while循环体中不具备有原子性。所以得出来的结果会出问题.在while循环体中加入synchronized块可以解决
NewMoons 2016-08-27
  • 打赏
  • 举报
回复
啥也不说直接上代码。
/**
 * 模拟N个线程同时执行任务,每个线程执行完自己的任务后做一个计数,所有线程结束后统计一共执行了多少个任务。
 * (原则上应该是一个线程对应一个任务,因此运行了多少个线程就应该是执行了多少个任务)。
 */
public class ThreadSyn {
	//任务执行计数器
	public int number = 0;
	
	//模拟执行任务
	public void doTask(){
		try {
			//模拟任务执行消耗的时间
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		//在没有加同步锁的情况下进行计数,执行完后注释这行,打开下面的三行代码,看看结果有何不同。
		number++;
		
		//在加了同步锁的情况下进行计数,执行完后注释这三行,打开上面的一行代码,看看结果有何不同。
//		synchronized(this){
//			number++;
//		}
	}
	
	public static void main(String[] args) {
		final ThreadSyn syn = new ThreadSyn();
		syn.number = 0;
		
		//模拟100个线程同时执行任务。
		for(int i=0;i<100;i++){
			new Thread(new Runnable(){
				@Override
				public void run() {
					syn.doTask();
				}
			}).start();
		}
		
		//这段代码的目的是保证上面的100个线程都执行完毕才退出循环,保证后面取到的number是线程执行完的计数结果。
		while(Thread.activeCount()>1){
			Thread.yield();
		}
		
		//打印执行了多少个任务。
		System.out.println(syn.number);
	}
}
林守中 2016-08-27
  • 打赏
  • 举报
回复
引用 5 楼 longzaierlian 的回复:
[quote=引用 3 楼 ddddouche 的回复:] 比如操作数据库,一个人在读取数据的时候,另外一个人在窜改你正在读的数据。这样安全吗?你所贴的代码里面,每个ticketNum都是每条线程都是自己的,这样肯定是线程安全的。可以试着把ticketNum改成全局变量, 然后用几条线程对它进行操作就知道为什么需要线程间数据同步了。
我用一个类对象,创建了四个线程, TicketWin win=new TicketWin(); Thread win1=new Thread(win); Thread win2=new Thread(win); Thread win3=new Thread(win); Thread win4=new Thread(win); 这四个线程操作的ticketNum是唯一的,这个就是共享的资源了。只不过这四个线程对ticketNum进行的操作是一样的,都是"--"。 我想知道的问题是这四个线程是如何轮流对共享变量ticketNum进行减减操作的。 这个例子已经体现了线程不安全的问题,因为如果线程是安全的,那么结果就会使顺序的9 8 7 6 5 4 ……了,而不会出现四个线程打印相同的值的情况了。[/quote] 事实上在线程进行处理的时候,几个线程并没有处理同一块内存,线程的实现会现在自己的工作线程中创建ticketNum的副本,处理完成后再写回主线程。
longzaierlian 2016-08-27
  • 打赏
  • 举报
回复
引用 3 楼 ddddouche 的回复:
比如操作数据库,一个人在读取数据的时候,另外一个人在窜改你正在读的数据。这样安全吗?你所贴的代码里面,每个ticketNum都是每条线程都是自己的,这样肯定是线程安全的。可以试着把ticketNum改成全局变量, 然后用几条线程对它进行操作就知道为什么需要线程间数据同步了。
我用一个类对象,创建了四个线程, TicketWin win=new TicketWin(); Thread win1=new Thread(win); Thread win2=new Thread(win); Thread win3=new Thread(win); Thread win4=new Thread(win); 这四个线程操作的ticketNum是唯一的,这个就是共享的资源了。只不过这四个线程对ticketNum进行的操作是一样的,都是"--"。 我想知道的问题是这四个线程是如何轮流对共享变量ticketNum进行减减操作的。 这个例子已经体现了线程不安全的问题,因为如果线程是安全的,那么结果就会使顺序的9 8 7 6 5 4 ……了,而不会出现四个线程打印相同的值的情况了。
codingjav 2016-08-27
  • 打赏
  • 举报
回复
引用 2 楼 rickylin86 的回复:
所谓的线程安全与不安全主要注重点在于多个线程同时对共享变量进行操作。 像你上面的代码中其实上每个线程操作的变量都是自身的,并不存在共享变量.所以是安全的.
对的,那是多个线程对同一变量进行操作的
longzaierlian 2016-08-27
  • 打赏
  • 举报
回复
引用
四个线程是轮流对共享变量ticketNum这个是无序的,看CPU的线程切换。
请问线程切换时怎么查看的,求指教,感谢。
ddddouche 2016-08-27
  • 打赏
  • 举报
回复
引用 5 楼 longzaierlian 的回复:
[quote=引用 3 楼 ddddouche 的回复:] 比如操作数据库,一个人在读取数据的时候,另外一个人在窜改你正在读的数据。这样安全吗?你所贴的代码里面,每个ticketNum都是每条线程都是自己的,这样肯定是线程安全的。可以试着把ticketNum改成全局变量, 然后用几条线程对它进行操作就知道为什么需要线程间数据同步了。
我用一个类对象,创建了四个线程, TicketWin win=new TicketWin(); Thread win1=new Thread(win); Thread win2=new Thread(win); Thread win3=new Thread(win); Thread win4=new Thread(win); 这四个线程操作的ticketNum是唯一的,这个就是共享的资源了。只不过这四个线程对ticketNum进行的操作是一样的,都是"--"。 我想知道的问题是这四个线程是如何轮流对共享变量ticketNum进行减减操作的。 这个例子已经体现了线程不安全的问题,因为如果线程是安全的,那么结果就会使顺序的9 8 7 6 5 4 ……了,而不会出现四个线程打印相同的值的情况了。[/quote]
引用 5 楼 longzaierlian 的回复:
[quote=引用 3 楼 ddddouche 的回复:] 比如操作数据库,一个人在读取数据的时候,另外一个人在窜改你正在读的数据。这样安全吗?你所贴的代码里面,每个ticketNum都是每条线程都是自己的,这样肯定是线程安全的。可以试着把ticketNum改成全局变量, 然后用几条线程对它进行操作就知道为什么需要线程间数据同步了。
我用一个类对象,创建了四个线程, TicketWin win=new TicketWin(); Thread win1=new Thread(win); Thread win2=new Thread(win); Thread win3=new Thread(win); Thread win4=new Thread(win); 这四个线程操作的ticketNum是唯一的,这个就是共享的资源了。只不过这四个线程对ticketNum进行的操作是一样的,都是"--"。 我想知道的问题是这四个线程是如何轮流对共享变量ticketNum进行减减操作的。 这个例子已经体现了线程不安全的问题,因为如果线程是安全的,那么结果就会使顺序的9 8 7 6 5 4 ……了,而不会出现四个线程打印相同的值的情况了。[/quote] 四个线程是轮流对共享变量ticketNum这个是无序的,看CPU的线程切换。
ddddouche 2016-08-26
  • 打赏
  • 举报
回复
比如操作数据库,一个人在读取数据的时候,另外一个人在窜改你正在读的数据。这样安全吗?你所贴的代码里面,每个ticketNum都是每条线程都是自己的,这样肯定是线程安全的。可以试着把ticketNum改成全局变量, 然后用几条线程对它进行操作就知道为什么需要线程间数据同步了。
rickylin86 2016-08-26
  • 打赏
  • 举报
回复
所谓的线程安全与不安全主要注重点在于多个线程同时对共享变量进行操作。 像你上面的代码中其实上每个线程操作的变量都是自身的,并不存在共享变量.所以是安全的.
soton_dolphin 2016-08-26
  • 打赏
  • 举报
回复
为什么最后那个输出 Thread-2 --- 2 是三个减号,而其他都是两个减号?

62,628

社区成员

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

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