您的位置:首页 > 其它

multithreading--线程不安全的案例,及其采用synchronized的解决办法

2015-10-29 20:39 387 查看

一:一个线程不安全的情况举例

public class J_xianchenganqaun {

/**多线程不可控因素:1,线程调度将时间片段分配给哪个线程;2,时间片段的具体时间。
* @param args
*/
public static void main(String[] args) {
final Table table=new Table();
Thread t1=new Thread(){
public void run(){
while(true){
/**
* 当beans=0时,run调用了getBean方法,
* 抛出了一个未解决的异常,该线程被杀掉
*/
int bean=table.getBean();
System.out.println(getName()+":"+bean);
Thread.yield();
}
}
};
Thread t2=new Thread(){
public void run(){
while(true){
int bean=table.getBean();
System.out.println(getName()+":"+bean);
Thread.yield();
}
}
};
t1.start();
t2.start();
}

}
class Table{
private int beans=20;
public int getBean(){
if(beans==0){
throw new RuntimeException("没豆了");
}
Thread.yield();//当前线程主动放弃CPU时间片,且在当前运行的位置暂停
return beans--;
/**
* 线程不安全情况:
* 1,当程序运行到beans=1时,线程t1调用getBean方法
* 2,此时beans=1!=0;所以不抛出异常
* 3,接着,t1运行到Thread.yield();自动放弃CPU时间片,t1在这个位置暂停;
* 4,t1放弃CPU时间片后,t2调用getBean方法,此时仍然beans=1!=0
* 5,接着t2运行到Thread.yield(),t2也自动放弃时间片,t2也在这个位置暂停;
* 6,t2放弃后,t1接着暂停的位置继续运行,运行到return语句,返回1,beans变为0,但是错过了抛出异常
* 7,接着,t1的时间片过了,t2接着暂停的位置继续执行,t2执行到return,但是此时beans=0,所以返回0,beans变为-1
* 8,从此以后beans逐渐往负无穷靠拢,背离了现实意义。
*/
}
}


二:上面线程不安全情况的解决办法

/**
* 多线程并发访问同一资源时,会产生线程安全问题。
* 解决多线程并发安全问题的办法是:将异步的操作变为同步的.
* 所谓同步:按先后顺序执行
* 所谓异步:根本就没有先后的概念
* @author Administrator
*
*/
public class J_y {

public static void main(String[] args) {
final Table3 table=new Table3();
Thread t1=new Thread(){
public void run(){
while(true){
/**
* 当beans=0时,run调用了getBean方法,
* 抛出了一个未解决的异常,该线程被杀掉
*/
int bean=table.getBean();
System.out.println(getName()+":"+bean);
Thread.yield();
}
}
};
Thread t2=new Thread(){
public void run(){
while(true){
int bean=table.getBean();
System.out.println(getName()+":"+bean);
Thread.yield();
}
}
};
t1.start();
t2.start();
}

}
class Table3{
private int beans=20;
/**
* synchronized的作用是当线程t1调用该方法时,会加把锁,实际上是给对象加把锁
* 这样即使在getBean方法内部,t1的时间片用完了,t2也只能在getBean方法外面等着
* 也进不去,等时间片再次轮到t1,t1会继续执行,就是说只能有一个线程进入直到执行完位置,即将异步的操作变为同步的
* @return
*/
synchronized public int getBean(){
if(beans==0){
throw new RuntimeException("没豆了");
}
Thread.yield();//当前线程主动放弃CPU时间片
return beans--;
}
}


三:更加高效的解决方式

上面使用synchronized同步了整个方法,但是很多时候,只需要同步方法的局部就可解决安全问题,另外因为同步的范围小了,多线程并发执行的效率也会增加,因此可以采用同步块的方式解决线程安全问题。
public class K_jianshiqi {

/**
* @param args
*/
public static void main(String[] args) {
final Table1 table=new Table1();
Thread t1=new Thread(){
public void run(){
while(true){
int bean=table.getBean();
System.out.println(getName()+":"+bean);
Thread.yield();
}
}
};
Thread t2=new Thread(){
public void run(){
while(true){
int bean=table.getBean();
System.out.println(getName()+":"+bean);
Thread.yield();
}
}
};
t1.start();
t2.start();
}

}
class Table1{
private int beans=20;
public int getBean(){
/**
* 同步块可以有效的缩小同步范围,从而提高并发执行效率。

使用同步块,必须要保证多线程看到的同步监视器这个对象是同一个!
*/
synchronized(this){//这里的this是table,t1和t2看到的是同一个,也可以换成任意字符串,但是不可以是new Object(),因为t1和t2会创建!=的对象
if(beans==0){
throw new RuntimeException("没豆了");
}
Thread.yield();
return beans--;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: