package thread;public class ProducerConsumerTest {
public static void main(String[] args) {
Queue queue = new Queue();
Producer p = new Producer(queue);
Consumer c = new Consumer(queue);
p.start();
c.start(); }}class Queue {
private int data;
private boolean isFull = false; public synchronized int get() {
if (!isFull) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isFull = false;
notify(); return data;
} public synchronized void set(int data) {
if (!isFull) {
this.data = data;
// System.out.println("Producer put data "+data);位置1
isFull = true;
notify();
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
} }}class Producer extends Thread {
Queue queue; Producer(Queue queue) {
this.queue = queue;
} public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("Producer put data " + i);//位置2。把这句代码从上面注释的地方挪到这个地方。
queue.set(i);
}
}
}class Consumer extends Thread {
Queue queue; Consumer(Queue queue) {
this.queue = queue;
} public void run() {
while (true) {
synchronized (queue) {
System.out.println("get data " + queue.get());//3
}
}
}
}//现在的问题是,把位置1的注释掉,使位置2生效后,同样线程同步。疑问是,应该存在这样的可能:在3只执行了queue.get()后,跳出,位置2获得执行,这样应该没有同步啊,也就是,put put 。
public static void main(String[] args) {
Queue queue = new Queue();
Producer p = new Producer(queue);
Consumer c = new Consumer(queue);
p.start();
c.start(); }}class Queue {
private int data;
private boolean isFull = false; public synchronized int get() {
if (!isFull) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isFull = false;
notify(); return data;
} public synchronized void set(int data) {
if (!isFull) {
this.data = data;
// System.out.println("Producer put data "+data);位置1
isFull = true;
notify();
}
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
} }}class Producer extends Thread {
Queue queue; Producer(Queue queue) {
this.queue = queue;
} public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("Producer put data " + i);//位置2。把这句代码从上面注释的地方挪到这个地方。
queue.set(i);
}
}
}class Consumer extends Thread {
Queue queue; Consumer(Queue queue) {
this.queue = queue;
} public void run() {
while (true) {
synchronized (queue) {
System.out.println("get data " + queue.get());//3
}
}
}
}//现在的问题是,把位置1的注释掉,使位置2生效后,同样线程同步。疑问是,应该存在这样的可能:在3只执行了queue.get()后,跳出,位置2获得执行,这样应该没有同步啊,也就是,put put 。
// TODO Auto-generated method stub
One one = new One();
Thread t = new MyTest(one);
Thread t2 = new MyTest2(one);
t2.start();
t.start();
}}class One {}class MyTest extends Thread {
One o; public MyTest(One o) {
this.o = o;
} public void run() { for (int i = 1; i <= 10; i++) {
System.out.println(i);
} }
}class MyTest2 extends Thread {
One o; public MyTest2(One o) {
this.o = o;
} public void run() {
synchronized (o) {
for (int i = 1; i <= 10; i++) {
System.out.println("syn" + i);
}
}
}
}
//在只有一个同步块时,另一个是非synchronizd修饰的run方法时,同步块不是原子操作,可以被其他线程打断。对上面问题的补充说明。
简单的说,比如
synchronized(A) {
System.out.println("A");
}
System.out.println("B");
这两个打印不一定能保证一致,因为线程(姑且叫这个线程为线程1)执行完synchronized块后,刚要打印System.out.println("B");的时候,系统可能把CPU收回,分配给其他线程(线程2线程3线程4等等),其他线程可能也会打印别的东西,过了一段时间线程1又获得CPU权,继续打印System.out.println("B");这样,System.out.println("A");和System.out.println("B");就不能保证是连续的,也就是说从System.out.println("A");到System.out.println("B");不一致,因为中间其他线程可能作了某些操作让结果发生变化了。
class Queue {
private int data;
private boolean isFull = false;public synchronized int get() {
if (!isFull) {
try {
System.out.println("有吗");
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
得到结果是:
Producer put data0
Producer put data0
get data0
有吗?
Producer put data1
Producer put data1
get data1
有吗?为什么在执行完get data()后它又去执行get data()里面的if语句块了?哪位大虾能否解释一下?谢谢!
阿宝分析的对。
另外我可能没有把问题表达清楚。补充一下。在位置1时,实现了同步容易理解;但放到位置2时,个人认为不应该实现同步啊(因为在只有一个同步块时,另一个是非synchronizd修饰的run方法时,同步块不是原子操作,可以被其他线程打断,上面有我给的例子),也就是存在这样的可能:在3只执行了queue.get()后,跳出,位置2获得执行,这样应该没有同步啊,也就是,put put 。但运行结果,似乎同样实现了同步。
可以假设一下你的程序执行
情况1 p先执行,打印2,假如p的cpu没被收回,那么p继续执行queue.set,会打印1,于是就会 put put,然后p发生wait,然后c执行会打印3(上面说了只有打印3结束c才会释放锁),**然后系统随机分配cpu给p或c,我们分a,b两种可能讨论
a)这时如果cpu分配给c,那么c会发生进入get的if (!isFull)发生wait,然后p执行,打印2,而c此时还在wait,所以p继续执行,打印1,于是有put put,然后唤醒c,p发生wait,然后c执行,会打印3;
b)如果这时cpu分配给p,那么p会打印2,这时我们分1)和2)两种可能讨论
1)p的cpu被收回分配给c,然后c执行get,进入if (!isFull)发生wait,然后p执行,打印1,于是发生put put,然后唤醒c,p发生wait,然后c执行,会打印3
2)p的cpu没被收回,继续打印1,于是有put put,然后唤醒c,p发生wait,然后c执行,会打印3;
接下来的情况,重复**情况2 p先执行,打印2,假如p的cpu被收回,c执行,那么c调用queue.get会进入if (!isFull)发生wait,于是p执行,打印1,所以就会 put put,然后唤醒c,p发生wait,c执行,会打印3,接下来同情况1的**情况3 c先执行,那么c调用queue.get会进入if (!isFull)发生wait,于是p执行,打印2,这时因为c还在wait中,所以p可以继续执行queue.set,打印1,所以就会 put put,然后唤醒c,p发生wait,c执行,会打印3,接下来的情况同情况1的**所以看上去,打印2好像也发生了同步,其实只是因为线程本身发生了等待而造成的,如果把2语句改在queue.set之后,那么你会发现打印2会发生在打印3之后,所以从结果上看(如果打印1注释掉),会觉得好像先发生了get才发生put,其实从程序上看,这是不可能,因为先get,没有数据会进入wait,但打印结果却是先get后put,这就说明打印2和set方法不同步不知道这么说LZ明白了吗?