我想实现一个两个线程对同一个变量,一个线程加,一个线程减的程序。可是打印的结果是1,2,3,4,5,6,7...而不是1,0,1,0..
想知道为什么
程序如下:
public class Core {
public static void main(String[] args) throws Exception{
Operation op = new Operation();
WorkerBear b1 = new WorkerBear(op);
EatBear b2 = new EatBear(op);
Thread t1 = new Thread(b1);
Thread t2 = new Thread(b2);
t2.start();
t1.start();
}
}public class WorkerBear implements Runnable{
private Operation op;
public WorkerBear(Operation op){
this.op = op;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
op.increment();
}
}
}public class EatBear implements Runnable{
private Operation op;
public EatBear(Operation op){
this.op = op;
} @Override
public void run() {
// TODO Auto-generated method stub
while(true){ //如果这里加入一个Thread.sleep()就好使,为什么呢
op.decrement();
}
}
}public class Operation {
int box = 0 ;
public synchronized void increment() {
box++;
try {
Thread.sleep(1000);
System.out.println(box);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized void decrement() {
box--;
try {
Thread.sleep(1000);
System.out.println(box);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
想知道为什么
程序如下:
public class Core {
public static void main(String[] args) throws Exception{
Operation op = new Operation();
WorkerBear b1 = new WorkerBear(op);
EatBear b2 = new EatBear(op);
Thread t1 = new Thread(b1);
Thread t2 = new Thread(b2);
t2.start();
t1.start();
}
}public class WorkerBear implements Runnable{
private Operation op;
public WorkerBear(Operation op){
this.op = op;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
op.increment();
}
}
}public class EatBear implements Runnable{
private Operation op;
public EatBear(Operation op){
this.op = op;
} @Override
public void run() {
// TODO Auto-generated method stub
while(true){ //如果这里加入一个Thread.sleep()就好使,为什么呢
op.decrement();
}
}
}public class Operation {
int box = 0 ;
public synchronized void increment() {
box++;
try {
Thread.sleep(1000);
System.out.println(box);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public synchronized void decrement() {
box--;
try {
Thread.sleep(1000);
System.out.println(box);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一个线程sleep的时候,没有释放锁,所以另一个线程无法执行
sleep完释放锁的时候,又没有做线程礼让,或唤醒其他线程的动作,系统把时间片仍然分给该线程,
所以就一直加加加
所以sleep(1000)要放在while循环中才行
所以必须把睡眠类放在带锁的方法的外面,睡眠是腾出足够的时间让 1, 0,1,0 出现规律些。
所以放在生产者和消费着的while里面,而且操作类不是线程类,不应该写睡眠方法啊。生产者和消费者
才都是线程类,你可以不睡眠看看效果,大致也能看出来。。
每个线程离开synchronized处理块就会释放锁,因为没有调用wait,所以系统会自动给线程分配CPU,哪个线程获得CPU权限,哪个线程就执行
造成这种巧合,可能是thread1刚sleep结束获得CPU继续执行的时候(比如系统分给thread1 0.5微妙CPU时间),因为打印语句很快就结束了,所以synchronized块结束以后(可能打印只需要0.3微妙),thread1的CPU分配时间还没有到(还剩0.2微妙),这样thread1继续wahile循环,又进入synchronized块了,所以thread2获得CPU时间的时候,因为thread1占用着锁,所以thread2还是不能执行像这种随机的结果,你多跑几次,时间再长一点,估计就能看到不同的结果了
*
*/
package cn.luochengor.csdn;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;/**
* 实现两个线程对同一个变量,一个线程加,一个线程减的程序 打印的结果是:1,0,1,0..
*
* @Author luochengor
* @Date Oct 25, 2011
* @Email [email protected]
*/
public class OneZero {
private int number = 0;
private boolean isOne = false; public synchronized int toOne() {
number++;
isOne = true;
notifyAll();
return number;
} public synchronized int toZero() {
number--;
isOne = false;
notifyAll();
return number;
} public synchronized void waitToOne() throws InterruptedException {
while (!isOne) {
wait();
}
} public synchronized void waitToZero() throws InterruptedException {
while (isOne) {
wait();
}
} /**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
OneZero oz = new OneZero();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ToOne(oz));
TimeUnit.MILLISECONDS.sleep(50);
exec.execute(new ToZero(oz));
TimeUnit.SECONDS.sleep(5);
exec.shutdownNow();
}}class ToOne implements Runnable {
private OneZero oz; public ToOne(OneZero oz) {
this.oz = oz;
} public void run() {
try {
while (!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(600);
System.out.println(oz.toOne());
oz.waitToZero();
}
} catch (InterruptedException e) {
System.out.println("InterruptedException class ToOne");
}
}
}class ToZero implements Runnable {
private OneZero oz; public ToZero(OneZero oz) {
this.oz = oz;
} public void run() {
try {
while (!Thread.interrupted()) {
TimeUnit.MILLISECONDS.sleep(600);
System.out.println(oz.toZero());
oz.waitToOne();
}
} catch (InterruptedException e) {
System.out.println("InterruptedException class ToZero");
}
}
}运行结果:1
0
1
0
1
0
1
0
1
InterruptedException class ToZero
InterruptedException class ToOne仅供参考,如有错误还望大家指教
其次是要明白Java的线程调度机制是依赖操作系统的,特别要了解抢占式调度机制(学习花费不了多少时间)。再回过头来看这些问题就非常清楚了。修改你的Operation类
示例:class Operation {
int box = 0 ;
boolean flag = true;
public synchronized void increment() {
if(flag == false){
try {
wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
notifyAll();
}
}
box++;
try {
Thread.sleep(1000);
System.out.println(box);
flag = false;
notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
notifyAll();
}
}
public synchronized void decrement() {
if(flag == true){
try {
wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
notifyAll();
}
}
box--;
try {
Thread.sleep(1000);
System.out.println(box);
flag = true;
notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
notifyAll();
}
}
}