java多线程之间通信
2017-07-22 14:12
176 查看
Java中线程间通信
1. 线程间通信(通讯): 多个线程在处理同一个资源,但任务不同。
一、等待/唤醒机制:
1. 涉及的方法:
(1) wait(); 使线程处于冻结状态,被wait的线程会存储到线程池(等待集)中;
(2) notify(); 唤醒线程池中的一个线程,这个线程是任意的;
(3) notifyAll(); 唤醒线程池中的所有线程。
2. 这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法;而且必须要明确到底操作的是哪个锁上的线程。即必须明确线程由那个监视器监视。
3. 监视器: 就是所说的锁。
4. 为什么操作线程的方法wait()、notify()、notifyAll()定义在了Object类中?
答:因为这些方法就是监视器的方法,监视器就是锁,锁可以是任意的对象,如Object obj = new Object();既然可以是任意对象,那么这些方法需要定义在最高的类中,即定义在Object类中。
5. 线程间通信的示例:
//创建一个资源类,存放资源
class Resource {
private String
name;
private String
sex;
private
boolean flag = false;
public
synchronized void input(String
name, String sex) {
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.name =
name;
this.sex =
sex;
flag = true;
this.notify();
}
public
synchronized void output() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(name +
" 的性别: " +
sex);
flag = false;
this.notify();
}
}
//创建输入的run方法,利用类对任务进行封装
class Input
implements Runnable {
Resource r = null;
Input(Resource r) {
this.r =
r;
}
public
void run() {
int
x = 0;
for (int
i = 10; i > 0;
i--) {
if (x == 0)
r.input("KangKang",
"男");
else
r.input("Jane",
"女");
x = (x + 1) % 2;
}
}
}
//利用类实现对输出任务的封装
class Output
implements Runnable {
Resource r = null;
Output(Resource r) {
this.r =
r;
}
public
void run() {
for (int
i = 10; i > 0;
i--)
r.output();
}
}
//主方法所在的类
public
class ThreadCommunication {
public
static void main(String[]
args) {
Resource r = new Resource();//创建一个资源对象,使下面两个线程同时操作同一个资源对象
Input in = new Input(r);
Output out = new Output(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
结果:(实现一次赋值,一次输出,两个线程交替进行)
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
6. 生产者—消费者模式:
(1)单生产者—单消费者模式:
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println("生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notify();
}
public
synchronized void eat() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println("消费者吃掉的 " +
this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notify();
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class ProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p
19954
= new Producer(r);
Consumer c = new Consumer(r);
Thread tp = new Thread(p);
Thread tc = new Thread(c);
tp.start();
tc.start();
}
}
(2)多生产者—多消费者模式:
① 将if换成while,解决了线程获取执行权后,再次判断flag,决定是否执行;if只能判断一次,判断后如果满足if句则线程wait,等到唤醒后直接执行下面的代码,不再判断;利用while可以解决这个问题,因为while是如果一直 为真,则一直循环,知道不满足才会执行下面的代码。While + notify会导致程序死锁。
② notifyAll解决了保证能够唤醒对方的线程,而notify只可以唤醒一个线程,可能仍然把本方的线程唤醒,然后本方判断flag不能跳出,导致所有的线程都冻结,产生死锁。
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
while (flag) //修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println(Thread.currentThread().getName()+"生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notifyAll();//将notify
换成 notifyAll
}
public
synchronized void eat() {
while(!flag)
//修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(Thread.currentThread().getName()+"消费者吃掉的 "
+ this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notifyAll();//将notify
换成 notifyAll
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class MultiProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p = new Producer(r);
Producer p1 = new Producer(r);
Consumer c = new Consumer(r);
Consumer c1 = new Consumer(r);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c1);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
7. Lock接口:的出现替代了同步代码块或者同步方法;将同步的隐式锁操作变成了显式的锁操作,变得更加灵活,并且可以在一个锁上设置多个监视器。Xxx.lock();方法获取锁,unlock();释放锁,通常将xxx.unlock();放在finally语句中。
8. Condition接口:的出现替代了Object类中的wait、notify、notifyAll方法,将这些监视器方法单独进行了封装,变成Condition监视器对象,可以和任意锁进行组合使用,方法变成await、signal、signalAll。
9. 同步代码块:对于锁的操作是隐式的。自从JDK1.5,Lock便取代了synchronized。
10. Sleep() 方法和wait() 方法 有什么区别?
答:
(1)Wait可以指定时间,也可以不指定时间,sleep必须指定时间;
(2)在同步中时,对于CPU的执行权和锁的处理不同:wait释放CPU执行权,释放锁,sleep释放CPU执行权,不释放锁(sleep不需要别人叫,wait需要别人叫,所以要释放锁)。
11. 在一个同步中,只能有一个线程执行,但是 可以在一个线程中被唤醒着多个线程,只有获得此同步锁的线程才会执行,被唤醒的其他线程且不具有锁的线程不会执行。
12. 线程停止的方法:①run方法结束;②stop方法。
13. 怎么控制线程的任务结束呢?
答:任务中设置有循环结构,只要控制住循环就可以控制住任务,即可以控制住线程。while(true){}是永久循环。 控制循环通常使用定义标志来完成。
如果线程处于冻结状态,无法读取标记时,怎样结束线程呢?
利用interrupt()方法,可以将线程从冻结状态强制回到拥有cpu资格的运行状态,但是这种强制动作会抛出异常InterruptedException,需要在catch中进行处理异常。
14. 守护线程:setDaemon(boolean b)方法可以将此线程设置为守护线程或用户线程。Thismethod must be invoked before the thread is started.该方法必须在调用start方法前调用。守护线程与一般线程运行、等待都一样,只不过结束的时候不一样,只要一般线程消失,守护线程会自动消失,无论其处于任何状态。当所有的线程都是守护线程时,此时程序会自动退出。
15. join()方法:作用:主线程停止,等待调用join方法的线程运行完毕后,再继续执行,注意主线程只等调用join方法的线程运行完毕,调用join方法也会抛出异常。临时加入一个线程可以使用join方法。
16. setPriority(int newPriority):设置优先级;数值只有1—10;默认的值是5.
17. 想要快速创建一个线程可以利用匿名内部类:
Runnable ru = new Runnable(){
***************public void run(){ }
};
new Thread(ru).start();
面试题:(1)class
Test3implements Runnable{
public
void run(Thread t) {
} }
请问有没有错误?有错误,错在第几行?
答:有错误,错在第一行,原因:要想实现接口必须实现接口中的所有方法,而接口Runnable的run方法是空参数的,所以此处并没有实现接口中的方法,如果没有实现接口中的方法,那么此类只能定义为抽象类,所以错在第一行,应该声明为abstract。
(2)public
class Test2 {
public
static void main(String[]
args) {
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAA11");//对接口进行匿名内部类
}
}) {//Thread作为父类,进行匿名内部类,下面的方法可以覆盖上面的封装了run方法的对象的run方法
public
void run() {
System.out.println("BBBBBBB11");
}
}.start(); //分析:一共存在三个run方法:①接口Runnable中的一个抽象的run方法,不可被调用;
//②Thread类实现了接口Runnable,故实现了其中的run方法,但是没有做任何动作,就是用来被重写的;
//③对接口进行匿名内部类,即创建一个类实现接口,实现了run方法;
//④把Thread类作为父类,以匿名内部类的方法创建一个子类,子类中对父类的run方法进行复写。
//对于子类来说,首先调用子类自己重写的run方法,其次是父类中以参数传递进来的对象的run方法,再其次是父类的run方法。
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAAAAAA22");
}
}) {//子类中并未重写run方法
}.start();
new Thread() {
}.start();//调用Thread类自己的run方法,是空的,什么也不执行
}
}
输出结果为:
BBBBBBB11
AAAAAAAAAAAA22
18. 多消费者—多生产者:
典型事例:
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
public
class BounderBuffer {
private
int putptr,takeptr,count;
private
final Lock lock = new ReentrantLock();
private
final Condition notFull =
lock.newCondition();
private
final Condition notEmpty =
lock.newCondition();
Object[] items = new Object[20];
public
void put(Object duck) {
lock.lock();
try {
while(count ==
items.length)
try {
notFull.await();
} catch (InterruptedException
e) {
}
items[putptr] =
duck;
if(++putptr ==
items.length)
putptr = 0;
count++;
System.out.println("put "+Thread.currentThread().getName()+" putptr = "+putptr
+ " count= "+count );
notEmpty.signal();
}
finally {
lock.unlock();
}
}
public Object take() {
lock.lock();
try {
while(count == 0)
try{
notEmpty.await();
}
catch(InterruptedException
e) {}
Object duck =
items[takeptr];
if(++takeptr ==
items.length)
takeptr = 0;
count--;
System.out.println("take "+Thread.currentThread().getName()+" takeptr = "+takeptr
+ " count= "+count );
notFull.signal();
return
duck;
}
finally {
lock.unlock();
}
}
}
class PutDuck
implements Runnable{
BounderBuffer b =
null;
PutDuck(BounderBuffer bou){
this.b =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--)
b.put(duck);
}
}
class TakeDuck
implements Runnable{
BounderBuffer bou =
null;
TakeDuck(BounderBuffer bou){
this.bou =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--) {
duck =
bou.take();
}
}
}
class Test{
public
static void main(String[]
args) {
BounderBuffer b =
newBounderBuffer();
PutDuck p = new PutDuck(b);
TakeDuck t = new TakeDuck(b);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
Java中线程间通信
1. 线程间通信(通讯): 多个线程在处理同一个资源,但任务不同。
一、等待/唤醒机制:
1. 涉及的方法:
(1) wait(); 使线程处于冻结状态,被wait的线程会存储到线程池(等待集)中;
(2) notify(); 唤醒线程池中的一个线程,这个线程是任意的;
(3) notifyAll(); 唤醒线程池中的所有线程。
2. 这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法;而且必须要明确到底操作的是哪个锁上的线程。即必须明确线程由那个监视器监视。
3. 监视器: 就是所说的锁。
4. 为什么操作线程的方法wait()、notify()、notifyAll()定义在了Object类中?
答:因为这些方法就是监视器的方法,监视器就是锁,锁可以是任意的对象,如Object obj = new Object();既然可以是任意对象,那么这些方法需要定义在最高的类中,即定义在Object类中。
5. 线程间通信的示例:
//创建一个资源类,存放资源
class Resource {
private String
name;
private String
sex;
private
boolean flag = false;
public
synchronized void input(String
name, String sex) {
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.name =
name;
this.sex =
sex;
flag = true;
this.notify();
}
public
synchronized void output() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(name +
" 的性别: " +
sex);
flag = false;
this.notify();
}
}
//创建输入的run方法,利用类对任务进行封装
class Input
implements Runnable {
Resource r = null;
Input(Resource r) {
this.r =
r;
}
public
void run() {
int
x = 0;
for (int
i = 10; i > 0;
i--) {
if (x == 0)
r.input("KangKang",
"男");
else
r.input("Jane",
"女");
x = (x + 1) % 2;
}
}
}
//利用类实现对输出任务的封装
class Output
implements Runnable {
Resource r = null;
Output(Resource r) {
this.r =
r;
}
public
void run() {
for (int
i = 10; i > 0;
i--)
r.output();
}
}
//主方法所在的类
public
class ThreadCommunication {
public
static void main(String[]
args) {
Resource r = new Resource();//创建一个资源对象,使下面两个线程同时操作同一个资源对象
Input in = new Input(r);
Output out = new Output(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
结果:(实现一次赋值,一次输出,两个线程交替进行)
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
6. 生产者—消费者模式:
(1)单生产者—单消费者模式:
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println("生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notify();
}
public
synchronized void eat() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println("消费者吃掉的 " +
this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notify();
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class ProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread tp = new Thread(p);
Thread tc = new Thread(c);
tp.start();
tc.start();
}
}
(2)多生产者—多消费者模式:
① 将if换成while,解决了线程获取执行权后,再次判断flag,决定是否执行;if只能判断一次,判断后如果满足if句则线程wait,等到唤醒后直接执行下面的代码,不再判断;利用while可以解决这个问题,因为while是如果一直 为真,则一直循环,知道不满足才会执行下面的代码。While + notify会导致程序死锁。
② notifyAll解决了保证能够唤醒对方的线程,而notify只可以唤醒一个线程,可能仍然把本方的线程唤醒,然后本方判断flag不能跳出,导致所有的线程都冻结,产生死锁。
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
while (flag) //修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println(Thread.currentThread().getName()+"生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notifyAll();//将notify
换成 notifyAll
}
public
synchronized void eat() {
while(!flag)
//修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(Thread.currentThread().getName()+"消费者吃掉的 "
+ this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notifyAll();//将notify
换成 notifyAll
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class MultiProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p = new Producer(r);
Producer p1 = new Producer(r);
Consumer c = new Consumer(r);
Consumer c1 = new Consumer(r);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c1);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
7. Lock接口:的出现替代了同步代码块或者同步方法;将同步的隐式锁操作变成了显式的锁操作,变得更加灵活,并且可以在一个锁上设置多个监视器。Xxx.lock();方法获取锁,unlock();释放锁,通常将xxx.unlock();放在finally语句中。
8. Condition接口:的出现替代了Object类中的wait、notify、notifyAll方法,将这些监视器方法单独进行了封装,变成Condition监视器对象,可以和任意锁进行组合使用,方法变成await、signal、signalAll。
9. 同步代码块:对于锁的操作是隐式的。自从JDK1.5,Lock便取代了synchronized。
10. Sleep() 方法和wait() 方法 有什么区别?
答:
(1)Wait可以指定时间,也可以不指定时间,sleep必须指定时间;
(2)在同步中时,对于CPU的执行权和锁的处理不同:wait释放CPU执行权,释放锁,sleep释放CPU执行权,不释放锁(sleep不需要别人叫,wait需要别人叫,所以要释放锁)。
11. 在一个同步中,只能有一个线程执行,但是 可以在一个线程中被唤醒着多个线程,只有获得此同步锁的线程才会执行,被唤醒的其他线程且不具有锁的线程不会执行。
12. 线程停止的方法:①run方法结束;②stop方法。
13. 怎么控制线程的任务结束呢?
答:任务中设置有循环结构,只要控制住循环就可以控制住任务,即可以控制住线程。while(true){}是永久循环。 控制循环通常使用定义标志来完成。
如果线程处于冻结状态,无法读取标记时,怎样结束线程呢?
利用interrupt()方法,可以将线程从冻结状态强制回到拥有cpu资格的运行状态,但是这种强制动作会抛出异常InterruptedException,需要在catch中进行处理异常。
14. 守护线程:setDaemon(boolean b)方法可以将此线程设置为守护线程或用户线程。Thismethod must be invoked before the thread is started.该方法必须在调用start方法前调用。守护线程与一般线程运行、等待都一样,只不过结束的时候不一样,只要一般线程消失,守护线程会自动消失,无论其处于任何状态。当所有的线程都是守护线程时,此时程序会自动退出。
15. join()方法:作用:主线程停止,等待调用join方法的线程运行完毕后,再继续执行,注意主线程只等调用join方法的线程运行完毕,调用join方法也会抛出异常。临时加入一个线程可以使用join方法。
16. setPriority(int newPriority):设置优先级;数值只有1—10;默认的值是5.
17. 想要快速创建一个线程可以利用匿名内部类:
Runnable ru = new Runnable(){
***************public void run(){ }
};
new Thread(ru).start();
面试题:(1)class
Test3implements Runnable{
public
void run(Thread t) {
} }
请问有没有错误?有错误,错在第几行?
答:有错误,错在第一行,原因:要想实现接口必须实现接口中的所有方法,而接口Runnable的run方法是空参数的,所以此处并没有实现接口中的方法,如果没有实现接口中的方法,那么此类只能定义为抽象类,所以错在第一行,应该声明为abstract。
(2)public
class Test2 {
public
static void main(String[]
args) {
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAA11");//对接口进行匿名内部类
}
}) {//Thread作为父类,进行匿名内部类,下面的方法可以覆盖上面的封装了run方法的对象的run方法
public
void run() {
System.out.println("BBBBBBB11");
}
}.start(); //分析:一共存在三个run方法:①接口Runnable中的一个抽象的run方法,不可被调用;
//②Thread类实现了接口Runnable,故实现了其中的run方法,但是没有做任何动作,就是用来被重写的;
//③对接口进行匿名内部类,即创建一个类实现接口,实现了run方法;
//④把Thread类作为父类,以匿名内部类的方法创建一个子类,子类中对父类的run方法进行复写。
//对于子类来说,首先调用子类自己重写的run方法,其次是父类中以参数传递进来的对象的run方法,再其次是父类的run方法。
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAAAAAA22");
}
}) {//子类中并未重写run方法
}.start();
new Thread() {
}.start();//调用Thread类自己的run方法,是空的,什么也不执行
}
}
输出结果为:
BBBBBBB11
AAAAAAAAAAAA22
18. 多消费者—多生产者:
典型事例:
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
public
class BounderBuffer {
private
int putptr,takeptr,count;
private
final Lock lock = new ReentrantLock();
private
final Condition notFull =
lock.newCondition();
private
final Condition notEmpty =
lock.newCondition();
Object[] items = new Object[20];
public
void put(Object duck) {
lock.lock();
try {
while(count ==
items.length)
try {
notFull.await();
} catch (InterruptedException
e) {
}
items[putptr] =
duck;
if(++putptr ==
items.length)
putptr = 0;
count++;
System.out.println("put "+Thread.currentThread().getName()+" putptr = "+putptr
+ " count= "+count );
notEmpty.signal();
}
finally {
lock.unlock();
}
}
public Object take() {
lock.lock();
try {
while(count == 0)
try{
notEmpty.await();
}
catch(InterruptedException
e) {}
Object duck =
items[takeptr];
if(++takeptr ==
items.length)
takeptr = 0;
count--;
System.out.println("take "+Thread.currentThread().getName()+" takeptr = "+takeptr
+ " count= "+count );
notFull.signal();
return
duck;
}
finally {
lock.unlock();
}
}
}
class PutDuck
implements Runnable{
BounderBuffer b =
null;
PutDuck(BounderBuffer bou){
this.b =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--)
b.put(duck);
}
}
class TakeDuck
implements Runnable{
BounderBuffer bou =
null;
TakeDuck(BounderBuffer bou){
this.bou =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--) {
duck =
bou.take();
}
}
}
class Test{
public
static void main(String[]
args) {
BounderBuffer b =
newBounderBuffer();
PutDuck p = new PutDuck(b);
TakeDuck t = new TakeDuck(b);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
1. 线程间通信(通讯): 多个线程在处理同一个资源,但任务不同。
一、等待/唤醒机制:
1. 涉及的方法:
(1) wait(); 使线程处于冻结状态,被wait的线程会存储到线程池(等待集)中;
(2) notify(); 唤醒线程池中的一个线程,这个线程是任意的;
(3) notifyAll(); 唤醒线程池中的所有线程。
2. 这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法;而且必须要明确到底操作的是哪个锁上的线程。即必须明确线程由那个监视器监视。
3. 监视器: 就是所说的锁。
4. 为什么操作线程的方法wait()、notify()、notifyAll()定义在了Object类中?
答:因为这些方法就是监视器的方法,监视器就是锁,锁可以是任意的对象,如Object obj = new Object();既然可以是任意对象,那么这些方法需要定义在最高的类中,即定义在Object类中。
5. 线程间通信的示例:
//创建一个资源类,存放资源
class Resource {
private String
name;
private String
sex;
private
boolean flag = false;
public
synchronized void input(String
name, String sex) {
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.name =
name;
this.sex =
sex;
flag = true;
this.notify();
}
public
synchronized void output() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(name +
" 的性别: " +
sex);
flag = false;
this.notify();
}
}
//创建输入的run方法,利用类对任务进行封装
class Input
implements Runnable {
Resource r = null;
Input(Resource r) {
this.r =
r;
}
public
void run() {
int
x = 0;
for (int
i = 10; i > 0;
i--) {
if (x == 0)
r.input("KangKang",
"男");
else
r.input("Jane",
"女");
x = (x + 1) % 2;
}
}
}
//利用类实现对输出任务的封装
class Output
implements Runnable {
Resource r = null;
Output(Resource r) {
this.r =
r;
}
public
void run() {
for (int
i = 10; i > 0;
i--)
r.output();
}
}
//主方法所在的类
public
class ThreadCommunication {
public
static void main(String[]
args) {
Resource r = new Resource();//创建一个资源对象,使下面两个线程同时操作同一个资源对象
Input in = new Input(r);
Output out = new Output(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
结果:(实现一次赋值,一次输出,两个线程交替进行)
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
6. 生产者—消费者模式:
(1)单生产者—单消费者模式:
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println("生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notify();
}
public
synchronized void eat() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println("消费者吃掉的 " +
this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notify();
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class ProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p
19954
= new Producer(r);
Consumer c = new Consumer(r);
Thread tp = new Thread(p);
Thread tc = new Thread(c);
tp.start();
tc.start();
}
}
(2)多生产者—多消费者模式:
① 将if换成while,解决了线程获取执行权后,再次判断flag,决定是否执行;if只能判断一次,判断后如果满足if句则线程wait,等到唤醒后直接执行下面的代码,不再判断;利用while可以解决这个问题,因为while是如果一直 为真,则一直循环,知道不满足才会执行下面的代码。While + notify会导致程序死锁。
② notifyAll解决了保证能够唤醒对方的线程,而notify只可以唤醒一个线程,可能仍然把本方的线程唤醒,然后本方判断flag不能跳出,导致所有的线程都冻结,产生死锁。
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
while (flag) //修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println(Thread.currentThread().getName()+"生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notifyAll();//将notify
换成 notifyAll
}
public
synchronized void eat() {
while(!flag)
//修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(Thread.currentThread().getName()+"消费者吃掉的 "
+ this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notifyAll();//将notify
换成 notifyAll
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class MultiProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p = new Producer(r);
Producer p1 = new Producer(r);
Consumer c = new Consumer(r);
Consumer c1 = new Consumer(r);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c1);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
7. Lock接口:的出现替代了同步代码块或者同步方法;将同步的隐式锁操作变成了显式的锁操作,变得更加灵活,并且可以在一个锁上设置多个监视器。Xxx.lock();方法获取锁,unlock();释放锁,通常将xxx.unlock();放在finally语句中。
8. Condition接口:的出现替代了Object类中的wait、notify、notifyAll方法,将这些监视器方法单独进行了封装,变成Condition监视器对象,可以和任意锁进行组合使用,方法变成await、signal、signalAll。
9. 同步代码块:对于锁的操作是隐式的。自从JDK1.5,Lock便取代了synchronized。
· 标准形式:
·
Lock l = ...;
·
l.lock();
·
try {
·
// access the resource protected by this lock
·
} finally {
·
l.unlock();
}
10. Sleep() 方法和wait() 方法 有什么区别?
答:
(1)Wait可以指定时间,也可以不指定时间,sleep必须指定时间;
(2)在同步中时,对于CPU的执行权和锁的处理不同:wait释放CPU执行权,释放锁,sleep释放CPU执行权,不释放锁(sleep不需要别人叫,wait需要别人叫,所以要释放锁)。
11. 在一个同步中,只能有一个线程执行,但是 可以在一个线程中被唤醒着多个线程,只有获得此同步锁的线程才会执行,被唤醒的其他线程且不具有锁的线程不会执行。
12. 线程停止的方法:①run方法结束;②stop方法。
13. 怎么控制线程的任务结束呢?
答:任务中设置有循环结构,只要控制住循环就可以控制住任务,即可以控制住线程。while(true){}是永久循环。 控制循环通常使用定义标志来完成。
如果线程处于冻结状态,无法读取标记时,怎样结束线程呢?
利用interrupt()方法,可以将线程从冻结状态强制回到拥有cpu资格的运行状态,但是这种强制动作会抛出异常InterruptedException,需要在catch中进行处理异常。
14. 守护线程:setDaemon(boolean b)方法可以将此线程设置为守护线程或用户线程。Thismethod must be invoked before the thread is started.该方法必须在调用start方法前调用。守护线程与一般线程运行、等待都一样,只不过结束的时候不一样,只要一般线程消失,守护线程会自动消失,无论其处于任何状态。当所有的线程都是守护线程时,此时程序会自动退出。
15. join()方法:作用:主线程停止,等待调用join方法的线程运行完毕后,再继续执行,注意主线程只等调用join方法的线程运行完毕,调用join方法也会抛出异常。临时加入一个线程可以使用join方法。
16. setPriority(int newPriority):设置优先级;数值只有1—10;默认的值是5.
17. 想要快速创建一个线程可以利用匿名内部类:
Runnable ru = new Runnable(){
***************public void run(){ }
};
new Thread(ru).start();
面试题:(1)class
Test3implements Runnable{
public
void run(Thread t) {
} }
请问有没有错误?有错误,错在第几行?
答:有错误,错在第一行,原因:要想实现接口必须实现接口中的所有方法,而接口Runnable的run方法是空参数的,所以此处并没有实现接口中的方法,如果没有实现接口中的方法,那么此类只能定义为抽象类,所以错在第一行,应该声明为abstract。
(2)public
class Test2 {
public
static void main(String[]
args) {
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAA11");//对接口进行匿名内部类
}
}) {//Thread作为父类,进行匿名内部类,下面的方法可以覆盖上面的封装了run方法的对象的run方法
public
void run() {
System.out.println("BBBBBBB11");
}
}.start(); //分析:一共存在三个run方法:①接口Runnable中的一个抽象的run方法,不可被调用;
//②Thread类实现了接口Runnable,故实现了其中的run方法,但是没有做任何动作,就是用来被重写的;
//③对接口进行匿名内部类,即创建一个类实现接口,实现了run方法;
//④把Thread类作为父类,以匿名内部类的方法创建一个子类,子类中对父类的run方法进行复写。
//对于子类来说,首先调用子类自己重写的run方法,其次是父类中以参数传递进来的对象的run方法,再其次是父类的run方法。
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAAAAAA22");
}
}) {//子类中并未重写run方法
}.start();
new Thread() {
}.start();//调用Thread类自己的run方法,是空的,什么也不执行
}
}
输出结果为:
BBBBBBB11
AAAAAAAAAAAA22
18. 多消费者—多生产者:
典型事例:
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
public
class BounderBuffer {
private
int putptr,takeptr,count;
private
final Lock lock = new ReentrantLock();
private
final Condition notFull =
lock.newCondition();
private
final Condition notEmpty =
lock.newCondition();
Object[] items = new Object[20];
public
void put(Object duck) {
lock.lock();
try {
while(count ==
items.length)
try {
notFull.await();
} catch (InterruptedException
e) {
}
items[putptr] =
duck;
if(++putptr ==
items.length)
putptr = 0;
count++;
System.out.println("put "+Thread.currentThread().getName()+" putptr = "+putptr
+ " count= "+count );
notEmpty.signal();
}
finally {
lock.unlock();
}
}
public Object take() {
lock.lock();
try {
while(count == 0)
try{
notEmpty.await();
}
catch(InterruptedException
e) {}
Object duck =
items[takeptr];
if(++takeptr ==
items.length)
takeptr = 0;
count--;
System.out.println("take "+Thread.currentThread().getName()+" takeptr = "+takeptr
+ " count= "+count );
notFull.signal();
return
duck;
}
finally {
lock.unlock();
}
}
}
class PutDuck
implements Runnable{
BounderBuffer b =
null;
PutDuck(BounderBuffer bou){
this.b =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--)
b.put(duck);
}
}
class TakeDuck
implements Runnable{
BounderBuffer bou =
null;
TakeDuck(BounderBuffer bou){
this.bou =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--) {
duck =
bou.take();
}
}
}
class Test{
public
static void main(String[]
args) {
BounderBuffer b =
newBounderBuffer();
PutDuck p = new PutDuck(b);
TakeDuck t = new TakeDuck(b);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
Java中线程间通信
1. 线程间通信(通讯): 多个线程在处理同一个资源,但任务不同。
一、等待/唤醒机制:
1. 涉及的方法:
(1) wait(); 使线程处于冻结状态,被wait的线程会存储到线程池(等待集)中;
(2) notify(); 唤醒线程池中的一个线程,这个线程是任意的;
(3) notifyAll(); 唤醒线程池中的所有线程。
2. 这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法;而且必须要明确到底操作的是哪个锁上的线程。即必须明确线程由那个监视器监视。
3. 监视器: 就是所说的锁。
4. 为什么操作线程的方法wait()、notify()、notifyAll()定义在了Object类中?
答:因为这些方法就是监视器的方法,监视器就是锁,锁可以是任意的对象,如Object obj = new Object();既然可以是任意对象,那么这些方法需要定义在最高的类中,即定义在Object类中。
5. 线程间通信的示例:
//创建一个资源类,存放资源
class Resource {
private String
name;
private String
sex;
private
boolean flag = false;
public
synchronized void input(String
name, String sex) {
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.name =
name;
this.sex =
sex;
flag = true;
this.notify();
}
public
synchronized void output() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(name +
" 的性别: " +
sex);
flag = false;
this.notify();
}
}
//创建输入的run方法,利用类对任务进行封装
class Input
implements Runnable {
Resource r = null;
Input(Resource r) {
this.r =
r;
}
public
void run() {
int
x = 0;
for (int
i = 10; i > 0;
i--) {
if (x == 0)
r.input("KangKang",
"男");
else
r.input("Jane",
"女");
x = (x + 1) % 2;
}
}
}
//利用类实现对输出任务的封装
class Output
implements Runnable {
Resource r = null;
Output(Resource r) {
this.r =
r;
}
public
void run() {
for (int
i = 10; i > 0;
i--)
r.output();
}
}
//主方法所在的类
public
class ThreadCommunication {
public
static void main(String[]
args) {
Resource r = new Resource();//创建一个资源对象,使下面两个线程同时操作同一个资源对象
Input in = new Input(r);
Output out = new Output(r);
Thread tin = new Thread(in);
Thread tout = new Thread(out);
tin.start();
tout.start();
}
}
结果:(实现一次赋值,一次输出,两个线程交替进行)
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
KangKang的性别: 男
Jane 的性别: 女
6. 生产者—消费者模式:
(1)单生产者—单消费者模式:
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
if (flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println("生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notify();
}
public
synchronized void eat() {
if (!flag)
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println("消费者吃掉的 " +
this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notify();
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class ProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread tp = new Thread(p);
Thread tc = new Thread(c);
tp.start();
tc.start();
}
}
(2)多生产者—多消费者模式:
① 将if换成while,解决了线程获取执行权后,再次判断flag,决定是否执行;if只能判断一次,判断后如果满足if句则线程wait,等到唤醒后直接执行下面的代码,不再判断;利用while可以解决这个问题,因为while是如果一直 为真,则一直循环,知道不满足才会执行下面的代码。While + notify会导致程序死锁。
② notifyAll解决了保证能够唤醒对方的线程,而notify只可以唤醒一个线程,可能仍然把本方的线程唤醒,然后本方判断flag不能跳出,导致所有的线程都冻结,产生死锁。
class Resource1 {
private
int count = 0;//
生产物品编号
private String
name;
private
boolean flag = false;
public
synchronized void produce(Stringname) {
this.name =
name;
while (flag) //修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
this.count++;
System.out.println(Thread.currentThread().getName()+"生产的 " +
this.name +
" 的编号是: " +
count);
flag = true;
this.notifyAll();//将notify
换成 notifyAll
}
public
synchronized void eat() {
while(!flag)
//修改:将if换成while
try {
this.wait();
} catch (InterruptedException
e) {
}
System.out.println(Thread.currentThread().getName()+"消费者吃掉的 "
+ this.name +
" 的编号是: "
+ this.count);
flag = false;
this.notifyAll();//将notify
换成 notifyAll
}
}
class Producer
implements Runnable {
private Resource1
r = null;
Producer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--) {
r.produce("烤鸭");
}
}
}
class Consumer
implements Runnable {
private Resource1
r = null;
Consumer(Resource1 r) {
this.r =
r;
}
public
void run() {
for (int
i = 100; i > 0;
i--)
r.eat();
}
}
public
class MultiProducerConsumerDemo {
public
static void main(String[]
args) {
Resource1 r = new Resource1();
Producer p = new Producer(r);
Producer p1 = new Producer(r);
Consumer c = new Consumer(r);
Consumer c1 = new Consumer(r);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c);
Thread t3 = new Thread(c1);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
7. Lock接口:的出现替代了同步代码块或者同步方法;将同步的隐式锁操作变成了显式的锁操作,变得更加灵活,并且可以在一个锁上设置多个监视器。Xxx.lock();方法获取锁,unlock();释放锁,通常将xxx.unlock();放在finally语句中。
8. Condition接口:的出现替代了Object类中的wait、notify、notifyAll方法,将这些监视器方法单独进行了封装,变成Condition监视器对象,可以和任意锁进行组合使用,方法变成await、signal、signalAll。
9. 同步代码块:对于锁的操作是隐式的。自从JDK1.5,Lock便取代了synchronized。
· 标准形式:
·
Lock l = ...;
·
l.lock();
·
try {
·
// access the resource protected by this lock
·
} finally {
·
l.unlock();
}
10. Sleep() 方法和wait() 方法 有什么区别?
答:
(1)Wait可以指定时间,也可以不指定时间,sleep必须指定时间;
(2)在同步中时,对于CPU的执行权和锁的处理不同:wait释放CPU执行权,释放锁,sleep释放CPU执行权,不释放锁(sleep不需要别人叫,wait需要别人叫,所以要释放锁)。
11. 在一个同步中,只能有一个线程执行,但是 可以在一个线程中被唤醒着多个线程,只有获得此同步锁的线程才会执行,被唤醒的其他线程且不具有锁的线程不会执行。
12. 线程停止的方法:①run方法结束;②stop方法。
13. 怎么控制线程的任务结束呢?
答:任务中设置有循环结构,只要控制住循环就可以控制住任务,即可以控制住线程。while(true){}是永久循环。 控制循环通常使用定义标志来完成。
如果线程处于冻结状态,无法读取标记时,怎样结束线程呢?
利用interrupt()方法,可以将线程从冻结状态强制回到拥有cpu资格的运行状态,但是这种强制动作会抛出异常InterruptedException,需要在catch中进行处理异常。
14. 守护线程:setDaemon(boolean b)方法可以将此线程设置为守护线程或用户线程。Thismethod must be invoked before the thread is started.该方法必须在调用start方法前调用。守护线程与一般线程运行、等待都一样,只不过结束的时候不一样,只要一般线程消失,守护线程会自动消失,无论其处于任何状态。当所有的线程都是守护线程时,此时程序会自动退出。
15. join()方法:作用:主线程停止,等待调用join方法的线程运行完毕后,再继续执行,注意主线程只等调用join方法的线程运行完毕,调用join方法也会抛出异常。临时加入一个线程可以使用join方法。
16. setPriority(int newPriority):设置优先级;数值只有1—10;默认的值是5.
17. 想要快速创建一个线程可以利用匿名内部类:
Runnable ru = new Runnable(){
***************public void run(){ }
};
new Thread(ru).start();
面试题:(1)class
Test3implements Runnable{
public
void run(Thread t) {
} }
请问有没有错误?有错误,错在第几行?
答:有错误,错在第一行,原因:要想实现接口必须实现接口中的所有方法,而接口Runnable的run方法是空参数的,所以此处并没有实现接口中的方法,如果没有实现接口中的方法,那么此类只能定义为抽象类,所以错在第一行,应该声明为abstract。
(2)public
class Test2 {
public
static void main(String[]
args) {
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAA11");//对接口进行匿名内部类
}
}) {//Thread作为父类,进行匿名内部类,下面的方法可以覆盖上面的封装了run方法的对象的run方法
public
void run() {
System.out.println("BBBBBBB11");
}
}.start(); //分析:一共存在三个run方法:①接口Runnable中的一个抽象的run方法,不可被调用;
//②Thread类实现了接口Runnable,故实现了其中的run方法,但是没有做任何动作,就是用来被重写的;
//③对接口进行匿名内部类,即创建一个类实现接口,实现了run方法;
//④把Thread类作为父类,以匿名内部类的方法创建一个子类,子类中对父类的run方法进行复写。
//对于子类来说,首先调用子类自己重写的run方法,其次是父类中以参数传递进来的对象的run方法,再其次是父类的run方法。
new Thread(new Runnable() {
public
void run() {
System.out.println("AAAAAAAAAAAA22");
}
}) {//子类中并未重写run方法
}.start();
new Thread() {
}.start();//调用Thread类自己的run方法,是空的,什么也不执行
}
}
输出结果为:
BBBBBBB11
AAAAAAAAAAAA22
18. 多消费者—多生产者:
典型事例:
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.Lock;
importjava.util.concurrent.locks.ReentrantLock;
public
class BounderBuffer {
private
int putptr,takeptr,count;
private
final Lock lock = new ReentrantLock();
private
final Condition notFull =
lock.newCondition();
private
final Condition notEmpty =
lock.newCondition();
Object[] items = new Object[20];
public
void put(Object duck) {
lock.lock();
try {
while(count ==
items.length)
try {
notFull.await();
} catch (InterruptedException
e) {
}
items[putptr] =
duck;
if(++putptr ==
items.length)
putptr = 0;
count++;
System.out.println("put "+Thread.currentThread().getName()+" putptr = "+putptr
+ " count= "+count );
notEmpty.signal();
}
finally {
lock.unlock();
}
}
public Object take() {
lock.lock();
try {
while(count == 0)
try{
notEmpty.await();
}
catch(InterruptedException
e) {}
Object duck =
items[takeptr];
if(++takeptr ==
items.length)
takeptr = 0;
count--;
System.out.println("take "+Thread.currentThread().getName()+" takeptr = "+takeptr
+ " count= "+count );
notFull.signal();
return
duck;
}
finally {
lock.unlock();
}
}
}
class PutDuck
implements Runnable{
BounderBuffer b =
null;
PutDuck(BounderBuffer bou){
this.b =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--)
b.put(duck);
}
}
class TakeDuck
implements Runnable{
BounderBuffer bou =
null;
TakeDuck(BounderBuffer bou){
this.bou =
bou;
}
private Object
duck = new Object();
public
void run() {
for(int
i = 100;i > 0;i--) {
duck =
bou.take();
}
}
}
class Test{
public
static void main(String[]
args) {
BounderBuffer b =
newBounderBuffer();
PutDuck p = new PutDuck(b);
TakeDuck t = new TakeDuck(b);
Thread t0 = new Thread(p);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
Thread t5 = new Thread(t);
t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
相关文章推荐
- java个人学习笔记18(多线程之间通信+等待唤醒机制)
- 对于JAVA多线程中线程之间的通信方式的理解
- Java 多线程编程之六:线程之间的通信(附源代码)
- 黑马程序员:Java基础——多线程之间的通信
- java多线程---线程之间的通信
- Java多线程之间通信——修改对象中的成员变量
- Java 多线程之间的通信
- Java多线程(3) 线程之间通信
- 应用java多线程实现服务器端与多客户端之间的通信
- 学习java多线程的笔记3-使用BlockingQueue阻塞队列来模拟两个线程之间的通信
- java多线程之间的相互通信
- java多线程实现服务器端与多客户端之间的通信
- 应用java多线程实现server端与多client之间的通信
- java使用轮询和wait()/notify()实现多线程之间的通信
- Java 多线程编程之六:线程之间的通信(附源代码)
- JAVA基础学习(十二)--多线程一线程之间的通信
- Java 多线程编程之六:线程之间的通信(附源代码)
- java基础——多线程之间通信
- Java---17---多线程之间的通信
- java多线程之间的通信