[hyddd的FindBugs分析记录][M M IS] Inconsistent synchronization追加说明
2009-04-01 20:44 hyddd 阅读(3718) 评论(3) 编辑 收藏 举报前面已经写了一篇文档说明Inconsistent synchronization,但最近做代码安全时候又发现了一些关于Inconsistent synchronization的新问题,所以追加说明一下。
我们先看一段较长的代码:
public class Test {
public static void main(String[] args) throws InterruptedException{
Test2 t2 = new Test2();
MyThread thread1 = new MyThread(t2,1);
t1.start();
for(int i=0;i!=1000;i++){
new MyThread(t2,2).start();
}
Thread.sleep(4000);
Integer a = t2.getA();
System.out.println("total:" + a);
}
}
class MyThread extends Thread {
public Test2 t2 = null;
public int num =0;
public MyThread(Test2 t2 , int num){
this.t2 = t2;
this.num =num;
}
public void run(){
if(num == 1){
try {
Integer a = t2.getA();
Thread.sleep(50);
t2.cleanA();
System.out.println("before clean:"+ a.toString());
} catch (InterruptedException e) {e.printStackTrace();}
}
else if(num == 2){
try{
t2.selfPlusPlus();
Thread.sleep(1);
}
catch (Exception e) { e.printStackTrace(); }
}
else
{
System.out.println("not run!");
}
}
}
class Test2 extends Thread {
private int a =100;
public synchronized void selfPlusPlus(){ a++; }
public synchronized void cleanA(){ a=0;}
public int getA() { return a; }
}
运行后我们得出一个结果:
public static void main(String[] args) throws InterruptedException{
Test2 t2 = new Test2();
MyThread thread1 = new MyThread(t2,1);
t1.start();
for(int i=0;i!=1000;i++){
new MyThread(t2,2).start();
}
Thread.sleep(4000);
Integer a = t2.getA();
System.out.println("total:" + a);
}
}
class MyThread extends Thread {
public Test2 t2 = null;
public int num =0;
public MyThread(Test2 t2 , int num){
this.t2 = t2;
this.num =num;
}
public void run(){
if(num == 1){
try {
Integer a = t2.getA();
Thread.sleep(50);
t2.cleanA();
System.out.println("before clean:"+ a.toString());
} catch (InterruptedException e) {e.printStackTrace();}
}
else if(num == 2){
try{
t2.selfPlusPlus();
Thread.sleep(1);
}
catch (Exception e) { e.printStackTrace(); }
}
else
{
System.out.println("not run!");
}
}
}
class Test2 extends Thread {
private int a =100;
public synchronized void selfPlusPlus(){ a++; }
public synchronized void cleanA(){ a=0;}
public int getA() { return a; }
}
before clean:100
total:835
Before clean =100,说明了我们的thread1是最先获得t2中的a的值!
我们的启了1000个线程去对t2做selfPlusPlus(a++)操作,正常是应该得到值是1000的,结果却是:835。
请看下面分析:
1.没有对变量 a做同步加锁,而是对操作的它的函数做同步加锁(如:public synchronized void selfPlusPlus(),public synchronized void cleanA()),这样做只能对同类操作加锁,不同类的操作,如:selfPlusPlus()和cleanA()互相之间同时对a的操作不会影响,看下面代码:
Public synchronized int test1(){
A = A+1;
//tag
A = A+1;
Return A
}
Public synchronized void test2(){
A=A-1;
}
cpu完全有可能在test1的"//tag"处进行线程切换,如果thread1和thread2都调用test1(),从thread1切换到thread2时,thread2发现资源被锁,则继续等待,cpu继续切换,然后thread1继续运行,但如果thread1调用test1(),而thread2调用teset2()呢,当在"//tag"处发生线程切换时,由于thread2资源没有被锁,故thread2可以正常执行,那么整个执行流程变为:A=A+1,A=A-1,A=A+1,结果是thread1的test1()返回了一个莫名其妙的数。A = A+1;
//tag
A = A+1;
Return A
}
Public synchronized void test2(){
A=A-1;
}
2.进行原子操时没有加锁,第一个例子中,其实,getA()和cleanA()两者应该是一个原子操作,但没对操作的对象a加锁,导致出现问题。
我建议把上述代码修改为:
public void run(){
if(num == 1){
try {
Synchronized(t2.lock){
Integer a = t2.getA();
Thread.sleep(50);
t2.cleanA();
System.out.println("before clean:"+ a.toString());
}
//
else if(num == 2){
try{
t2.selfPlusPlus();
Thread.sleep(1);
}
//
class Test2 extends Thread {
//
Object lock = new Object();
//
public void selfPlusPlus(){
Synchronized(lock){
a++;
}
}
//
if(num == 1){
try {
Synchronized(t2.lock){
Integer a = t2.getA();
Thread.sleep(50);
t2.cleanA();
System.out.println("before clean:"+ a.toString());
}
//
else if(num == 2){
try{
t2.selfPlusPlus();
Thread.sleep(1);
}
//
class Test2 extends Thread {
//
Object lock = new Object();
//
public void selfPlusPlus(){
Synchronized(lock){
a++;
}
}
//
作者:hyddd
出处:http://www.cnblogs.com/hyddd/
本文版权归作者所有,欢迎转载,演绎或用于商业目的,但是必须说明本文出处(包含链接)。