Java多线程要点分析总结 (二)

2014-11-24 11:10:36 · 作者: · 浏览: 4

ds.add(1);
}
});
threads[i].start();
}
}
}

class DoSomething {
private int a = 10;
public void add(int b) {
int temp = a; // 缓存现有值
temp += b; // 缓存累加后的值
try {
Thread.sleep(100); // 模拟处理数据的时间或者线程切换。
} catch (Exception e) { }
a = temp; // 模拟写入操作。
System.out.println(getA()); // 输出当前线程对a进行累加操作后的值
}
public int getA() {
return this.a;
}
}
public class ThreadDemo {
private static Thread[] threads = new Thread[10];

public static void main(String[] args) {
final DoSomething ds = new DoSomething();
for (int i=0; i<10; i++) { // 开启10条线程进行操作
threads[i] = new Thread(new Runnable(){
@Override
public void run() {
ds.add(1);
}
});
threads[i].start();
}
}
}观察以上程序,发现10个线程每次写入之后,a都等于11,10个线程写了10次,应该+10等于20啊?

为什么是11呢?详细分析一下:
比如A线程进入add方法开始操作数据(将累加的数据缓存给局部变量temp),完成了数据的缓存,还没修改a的值,A线程进入休眠状态,
然后B线程进入add方法开始操作数据(将累加的数据缓存给局部变量temp),完成了数据的缓存,还没修改a的值,B线程进入休眠状态,
然后A线程重新进入运行状态,完成了数据的修改操作,a=11;B线程进入运行状态,完成了对数据的修改操作,a=11。
哪里有问题?应该出来了吧?局部变量temp的值被后来的b线程覆盖掉了。所以,即使10个线程执行add方法,每次temp都是11。
解决方式:
对add方法进行同步。修改后,public synchronized void add(int b),这样便使用同步,解决了以上问题。输出如下:


例B<判断异常问题>
[java]
class DoSomething {
private int ticket = 50;

public void sell() {
for (int i=0; i<50; i++) {
if (ticket > 0) { // 还有票
try {
Thread.sleep(100); // 模拟处理数据的时间或者线程切换。
} catch (Exception e) {}
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket-- + "张票");
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
final DoSomething ds = new DoSomething();
for (int i=0; i<3; i++) { // 开三条线程开始卖票
new Thread(new Runnable(){
@Override
public void run() {
ds.sell();
}
}).start();
}
}
}

class DoSomething {
private int ticket = 50;

public void sell() {
for (int i=0; i<50; i++) {
if (ticket > 0) { // 还有票
try {
Thread.sleep(100); // 模拟处理数据的时间或者线程切换。
} catch (Exception e) {}
System.out.println(Thread.currentThread().getName() + "卖出第" + ticket-- + "张票");
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
final DoSomething ds = new DoSomething();
for (int i=0; i<3; i++) { // 开三条线程开始卖票
new Thread(new Runnable(){
@Override
public void run() {
ds.sell();
}
}).start();
}
}
}以上程序最后输出了-1;在哪出问题了呢?在判断ticket>0的时候,比如现在ticket=1,A线程进入判断大于0,然后休眠了。
B线程进入判断大于0,执行了ticket--,ticket成0了,A线程回到运行状态,执行了ticket--,ticket成-1了。
解决方法,对sell方法进行同步,或者对if判断使用同步代码块进行同步。


我们实现线程安全的方式是互斥同步,即使用原语synchronized进行同步。
Synchronized关键字:
编译后会在同步块前后分别形成monitorenter和monitorexit这两个字节码指令。
这两个指令都需要一个引用类型的参数来指明要锁定和解锁的对象。如果没有明确指定对象参数,
那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。
在执行monitorenter指令时,首先尝试获取对象的锁,如果没有被锁定或者当前线程已经拥有了该对象的锁,则将锁计数器加1,
相应的执行moniterexit时,将锁计数器减1,当计数器为0时,锁就被释放了。如果获取对象锁失败,则当前线程就要阻塞等待。

3:多线程死锁问题
死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。
可能发生在以下情况:
当两个线程相互调用Thread.join();
当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必须的锁,互相等待时被阻塞就有可能出现死锁。
写一个死锁出来:
[java]
class MyLock {
static final Object lockA = new Object();
static final Object lockB = new Object();
}

class DoSomething implements Runnable {
private boolean flag;

public DoSomething(boole