下面是我自己写的一个生产者消费者程序
/**
* 馒头
*/
class Mantou {
}
/**
* 仓库,用来存放馒头
*/
class GodOwn {
private List mantouList;
int max = 10;
GodOwn () {
this.mantouList = Lists.newArrayList();
}
/**
* 生产
*/
public synchronized void produce () {
System.out.println("进入生产线程..." + Thread.currentThread().getName());
if (this.mantouList.size() > 0 || this.mantouList.size() >= max - 3) {
try {
System.out.println("生产线程被阻塞,线程名称为:之前:" + Thread.currentThread().getName());
this.wait();
System.out.println("生产线程被阻塞,线程名称为:之后:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("生产线程在执行,线程名称为:" + Thread.currentThread().getName());
this.mantouList.add(new Mantou());
this.mantouList.add(new Mantou());
this.mantouList.add(new Mantou());
System.out.println("生产了3个馒头,现在的馒头数是:" + this.mantouList.size());
notifyAll();
}
/**
* 消费
*/
public synchronized void reduce () {
System.out.println("进入消费线程..." + Thread.currentThread().getName());
if (this.mantouList.size() <= 0) {
try {
System.out.println("消费线程被阻塞,线程名称为:之前:" + Thread.currentThread().getName());
this.wait();
System.out.println("消费线程被阻塞,线程名称为:之后:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("消费线程在执行,线程名称为:" + Thread.currentThread().getName());
this.mantouList.remove(0);
System.out.println("消费了1个馒头,现在的馒头数是:" + this.mantouList.size());
notifyAll();
}
}
/**
* 消费者
*/
class Customer extends Thread {
private GodOwn godOwn;
public Customer (GodOwn godOwn) {
this.godOwn = godOwn;
}
public void run () {
godOwn.reduce();
}
}
/**
* 生产者
*/
class Producer extends Thread {
private GodOwn godOwn;
public Producer (GodOwn godOwn) {
this.godOwn = godOwn;
}
public void run () {
godOwn.produce();
}
}
执行结果为:
进入生产线程...Thread-0
生产线程在执行,线程名称为:Thread-0
生产了3个馒头,现在的馒头数是:3
进入生产线程...Thread-4
生产线程被阻塞,线程名称为:之前:Thread-4
进入生产线程...Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
进入生产线程...Thread-6
生产线程被阻塞,线程名称为:之前:Thread-6
进入生产线程...Thread-8
生产线程被阻塞,线程名称为:之前:Thread-8
进入生产线程...Thread-1
生产线程被阻塞,线程名称为:之前:Thread-1
进入生产线程...Thread-3
生产线程被阻塞,线程名称为:之前:Thread-3
进入生产线程...Thread-5
生产线程被阻塞,线程名称为:之前:Thread-5
进入生产线程...Thread-7
生产线程被阻塞,线程名称为:之前:Thread-7
进入生产线程...Thread-9
生产线程被阻塞,线程名称为:之前:Thread-9
进入消费线程...Thread-10
消费线程在执行,线程名称为:Thread-10
消费了1个馒头,现在的馒头数是:2
生产线程被阻塞,线程名称为:之后:Thread-9
生产线程在执行,线程名称为:Thread-9
生产了3个馒头,现在的馒头数是:5
生产线程被阻塞,线程名称为:之后:Thread-7
生产线程在执行,线程名称为:Thread-7
生产了3个馒头,现在的馒头数是:8
进入消费线程...Thread-12
消费线程在执行,线程名称为:Thread-12
消费了1个馒头,现在的馒头数是:7
生产线程被阻塞,线程名称为:之后:Thread-5
生产线程在执行,线程名称为:Thread-5
生产了3个馒头,现在的馒头数是:10
生产线程被阻塞,线程名称为:之后:Thread-3
生产线程在执行,线程名称为:Thread-3
生产了3个馒头,现在的馒头数是:13
进入消费线程...Thread-14
消费线程在执行,线程名称为:Thread-14
消费了1个馒头,现在的馒头数是:12
生产线程被阻塞,线程名称为:之后:Thread-1
生产线程在执行,线程名称为:Thread-1
生产了3个馒头,现在的馒头数是:15
生产线程被阻塞,线程名称为:之后:Thread-8
生产线程在执行,线程名称为:Thread-8
生产了3个馒头,现在的馒头数是:18
生产线程被阻塞,线程名称为:之后:Thread-6
生产线程在执行,线程名称为:Thread-6
生产了3个馒头,现在的馒头数是:21
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程在执行,线程名称为:Thread-2
生产了3个馒头,现在的馒头数是:24
生产线程被阻塞,线程名称为:之后:Thread-4
生产线程在执行,线程名称为:Thread-4
生产了3个馒头,现在的馒头数是:27
进入消费线程...Thread-13
消费线程在执行,线程名称为:Thread-13
消费了1个馒头,现在的馒头数是:26
进入消费线程...Thread-15
消费线程在执行,线程名称为:Thread-15
消费了1个馒头,现在的馒头数是:25
进入消费线程...Thread-11
消费线程在执行,线程名称为:Thread-11
消费了1个馒头,现在的馒头数是:24
进入消费线程...Thread-17
消费线程在执行,线程名称为:Thread-17
消费了1个馒头,现在的馒头数是:23
进入消费线程...Thread-16
消费线程在执行,线程名称为:Thread-16
消费了1个馒头,现在的馒头数是:22
进入消费线程...Thread-19
消费线程在执行,线程名称为:Thread-19
消费了1个馒头,现在的馒头数是:21
进入消费线程...Thread-18
消费线程在执行,线程名称为:Thread-18
消费了1个馒头,现在的馒头数是:20
进入消费线程...Thread-20
消费线程在执行,线程名称为:Thread-20
消费了1个馒头,现在的馒头数是:19
进入消费线程...Thread-21
消费线程在执行,线程名称为:Thread-21
消费了1个馒头,现在的馒头数是:18
进入消费线程...Thread-22
消费线程在执行,线程名称为:Thread-22
消费了1个馒头,现在的馒头数是:17
进入消费线程...Thread-23
消费线程在执行,线程名称为:Thread-23
消费了1个馒头,现在的馒头数是:16
进入消费线程...Thread-24
消费线程在执行,线程名称为:Thread-24
消费了1个馒头,现在的馒头数是:15
进入消费线程...Thread-25
消费线程在执行,线程名称为:Thread-25
消费了1个馒头,现在的馒头数是:14
进入消费线程...Thread-26
消费线程在执行,线程名称为:Thread-26
消费了1个馒头,现在的馒头数是:13
进入消费线程...Thread-27
消费线程在执行,线程名称为:Thread-27
消费了1个馒头,现在的馒头数是:12
进入消费线程...Thread-28
消费线程在执行,线程名称为:Thread-28
消费了1个馒头,现在的馒头数是:11
进入消费线程...Thread-29
消费线程在执行,线程名称为:Thread-29
消费了1个馒头,现在的馒头数是:10
进入消费线程...Thread-30
消费线程在执行,线程名称为:Thread-30
消费了1个馒头,现在的馒头数是:9
进入消费线程...Thread-31
消费线程在执行,线程名称为:Thread-31
消费了1个馒头,现在的馒头数是:8
进入消费线程...Thread-32
消费线程在执行,线程名称为:Thread-32
消费了1个馒头,现在的馒头数是:7
进入消费线程...Thread-33
消费线程在执行,线程名称为:Thread-33
消费了1个馒头,现在的馒头数是:6
进入消费线程...Thread-34
消费线程在执行,线程名称为:Thread-34
消费了1个馒头,现在的馒头数是:5
进入消费线程...Thread-35
消费线程在执行,线程名称为:Thread-35
消费了1个馒头,现在的馒头数是:4
进入消费线程...Thread-37
消费线程在执行,线程名称为:Thread-37
消费了1个馒头,现在的馒头数是:3
进入消费线程...Thread-36
消费线程在执行,线程名称为:Thread-36
消费了1个馒头,现在的馒头数是:2
进入消费线程...Thread-38
消费线程在执行,线程名称为:Thread-38
消费了1个馒头,现在的馒头数是:1
进入消费线程...Thread-39
消费线程在执行,线程名称为:Thread-39
消费了1个馒头,现在的馒头数是:0
对于线程被notifyAll()以后,线程会进入等待cpu分配时间片,分配到后执行,那么被吵醒的线程从哪里执行代码呢?
有两种假设:
1 从同步代码块开始执行
2 从wait()方法后开始执行
根据执行结果我们可以判断,被吵醒的线程是从wait()方法后开始执行的,因为从打印结果我们可以排除第一种假设,因为每次进入方法的时候都会打印“进入生产线程...” + 线程名称,这一段,这一段在打印结果中是唯一的;
根据上面的打印结果看以看到, 打印信息“生产线程被阻塞,线程名称为:之后:” + 线程名称,和打印信息:“生产线程在执行,线程名称为:” + 线程名称都是紧挨着的,这说明当线程被吵醒之后是从wait()方法后开始执行的;
从上面的代码可以看到,在生产馒头的时候,是做最大数限制的,可是从打印结果可以看到,馒头数最多的时候有27个,也就是说我们的限制没起作用,根本原因是因为等待的线程被吵醒之后并没有重新执行同步代码块,
而是从wait()方法后的代码执行的
现在可以这么来控制代码来做最大限制控制:
/**
* 生产
*/
public synchronized void produce () {
System.out.println("进入生产线程..." + Thread.currentThread().getName());
while (this.mantouList.size() > 0 || this.mantouList.size() >= max - 3) {
try {
System.out.println("生产线程被阻塞,线程名称为:之前:" + Thread.currentThread().getName());
this.wait();
System.out.println("生产线程被阻塞,线程名称为:之后:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("生产线程在执行,线程名称为:" + Thread.currentThread().getName());
this.mantouList.add(new Mantou());
this.mantouList.add(new Mantou());
this.mantouList.add(new Mantou());
System.out.println("生产了3个馒头,现在的馒头数是:" + this.mantouList.size());
notifyAll();
}
/**
* 消费
*/
public synchronized void reduce () {
System.out.println("进入消费线程..." + Thread.currentThread().getName());
while (this.mantouList.size() <= 0) {
try {
System.out.println("消费线程被阻塞,线程名称为:之前:" + Thread.currentThread().getName());
this.wait();
System.out.println("消费线程被阻塞,线程名称为:之后:" + Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("消费线程在执行,线程名称为:" + Thread.currentThread().getName());
this.mantouList.remove(0);
System.out.println("消费了1个馒头,现在的馒头数是:" + this.mantouList.size());
notifyAll();
}
生产和消费方法和原来的区别就是判断线程阻塞的代码块由if改成while,执行结果如下:
进入生产线程...Thread-0
生产线程在执行,线程名称为:Thread-0
生产了3个馒头,现在的馒头数是:3
进入生产线程...Thread-8
生产线程被阻塞,线程名称为:之前:Thread-8
进入生产线程...Thread-9
生产线程被阻塞,线程名称为:之前:Thread-9
进入生产线程...Thread-6
生产线程被阻塞,线程名称为:之前:Thread-6
进入生产线程...Thread-7
生产线程被阻塞,线程名称为:之前:Thread-7
进入生产线程...Thread-5
生产线程被阻塞,线程名称为:之前:Thread-5
进入生产线程...Thread-4
生产线程被阻塞,线程名称为:之前:Thread-4
进入生产线程...Thread-3
生产线程被阻塞,线程名称为:之前:Thread-3
进入生产线程...Thread-1
生产线程被阻塞,线程名称为:之前:Thread-1
进入生产线程...Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
进入消费线程...Thread-10
消费线程在执行,线程名称为:Thread-10
消费了1个馒头,现在的馒头数是:2
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
生产线程被阻塞,线程名称为:之后:Thread-1
生产线程被阻塞,线程名称为:之前:Thread-1
进入消费线程...Thread-11
消费线程在执行,线程名称为:Thread-11
消费了1个馒头,现在的馒头数是:1
生产线程被阻塞,线程名称为:之后:Thread-3
生产线程被阻塞,线程名称为:之前:Thread-3
进入消费线程...Thread-12
消费线程在执行,线程名称为:Thread-12
消费了1个馒头,现在的馒头数是:0
生产线程被阻塞,线程名称为:之后:Thread-4
生产线程在执行,线程名称为:Thread-4
生产了3个馒头,现在的馒头数是:3
生产线程被阻塞,线程名称为:之后:Thread-5
生产线程被阻塞,线程名称为:之前:Thread-5
进入消费线程...Thread-13
消费线程在执行,线程名称为:Thread-13
消费了1个馒头,现在的馒头数是:2
进入消费线程...Thread-14
消费线程在执行,线程名称为:Thread-14
消费了1个馒头,现在的馒头数是:1
生产线程被阻塞,线程名称为:之后:Thread-7
生产线程被阻塞,线程名称为:之前:Thread-7
生产线程被阻塞,线程名称为:之后:Thread-6
生产线程被阻塞,线程名称为:之前:Thread-6
生产线程被阻塞,线程名称为:之后:Thread-9
生产线程被阻塞,线程名称为:之前:Thread-9
生产线程被阻塞,线程名称为:之后:Thread-8
生产线程被阻塞,线程名称为:之前:Thread-8
进入消费线程...Thread-16
消费线程在执行,线程名称为:Thread-16
消费了1个馒头,现在的馒头数是:0
进入消费线程...Thread-17
消费线程被阻塞,线程名称为:之前:Thread-17
进入消费线程...Thread-15
消费线程被阻塞,线程名称为:之前:Thread-15
生产线程被阻塞,线程名称为:之后:Thread-5
生产线程在执行,线程名称为:Thread-5
生产了3个馒头,现在的馒头数是:3
生产线程被阻塞,线程名称为:之后:Thread-3
生产线程被阻塞,线程名称为:之前:Thread-3
生产线程被阻塞,线程名称为:之后:Thread-1
生产线程被阻塞,线程名称为:之前:Thread-1
进入消费线程...Thread-19
消费线程在执行,线程名称为:Thread-19
消费了1个馒头,现在的馒头数是:2
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
生产线程被阻塞,线程名称为:之后:Thread-1
生产线程被阻塞,线程名称为:之前:Thread-1
生产线程被阻塞,线程名称为:之后:Thread-3
生产线程被阻塞,线程名称为:之前:Thread-3
消费线程被阻塞,线程名称为:之后:Thread-15
消费线程在执行,线程名称为:Thread-15
消费了1个馒头,现在的馒头数是:1
消费线程被阻塞,线程名称为:之后:Thread-17
消费线程在执行,线程名称为:Thread-17
消费了1个馒头,现在的馒头数是:0
生产线程被阻塞,线程名称为:之后:Thread-8
生产线程在执行,线程名称为:Thread-8
生产了3个馒头,现在的馒头数是:3
进入消费线程...Thread-20
消费线程在执行,线程名称为:Thread-20
消费了1个馒头,现在的馒头数是:2
进入消费线程...Thread-22
消费线程在执行,线程名称为:Thread-22
消费了1个馒头,现在的馒头数是:1
生产线程被阻塞,线程名称为:之后:Thread-9
生产线程被阻塞,线程名称为:之前:Thread-9
进入消费线程...Thread-26
消费线程在执行,线程名称为:Thread-26
消费了1个馒头,现在的馒头数是:0
生产线程被阻塞,线程名称为:之后:Thread-6
生产线程在执行,线程名称为:Thread-6
生产了3个馒头,现在的馒头数是:3
生产线程被阻塞,线程名称为:之后:Thread-7
生产线程被阻塞,线程名称为:之前:Thread-7
进入消费线程...Thread-27
消费线程在执行,线程名称为:Thread-27
消费了1个馒头,现在的馒头数是:2
进入消费线程...Thread-18
消费线程在执行,线程名称为:Thread-18
消费了1个馒头,现在的馒头数是:1
进入消费线程...Thread-28
消费线程在执行,线程名称为:Thread-28
消费了1个馒头,现在的馒头数是:0
进入消费线程...Thread-30
消费线程被阻塞,线程名称为:之前:Thread-30
生产线程被阻塞,线程名称为:之后:Thread-7
生产线程在执行,线程名称为:Thread-7
生产了3个馒头,现在的馒头数是:3
生产线程被阻塞,线程名称为:之后:Thread-9
生产线程被阻塞,线程名称为:之前:Thread-9
进入消费线程...Thread-29
消费线程在执行,线程名称为:Thread-29
消费了1个馒头,现在的馒头数是:2
进入消费线程...Thread-24
消费线程在执行,线程名称为:Thread-24
消费了1个馒头,现在的馒头数是:1
进入消费线程...Thread-25
消费线程在执行,线程名称为:Thread-25
消费了1个馒头,现在的馒头数是:0
生产线程被阻塞,线程名称为:之后:Thread-3
生产线程在执行,线程名称为:Thread-3
生产了3个馒头,现在的馒头数是:3
生产线程被阻塞,线程名称为:之后:Thread-1
生产线程被阻塞,线程名称为:之前:Thread-1
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
进入消费线程...Thread-23
消费线程在执行,线程名称为:Thread-23
消费了1个馒头,现在的馒头数是:2
进入消费线程...Thread-21
消费线程在执行,线程名称为:Thread-21
消费了1个馒头,现在的馒头数是:1
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
生产线程被阻塞,线程名称为:之后:Thread-1
生产线程被阻塞,线程名称为:之前:Thread-1
进入消费线程...Thread-34
消费线程在执行,线程名称为:Thread-34
消费了1个馒头,现在的馒头数是:0
进入消费线程...Thread-32
消费线程被阻塞,线程名称为:之前:Thread-32
进入消费线程...Thread-31
消费线程被阻塞,线程名称为:之前:Thread-31
进入消费线程...Thread-36
消费线程被阻塞,线程名称为:之前:Thread-36
生产线程被阻塞,线程名称为:之后:Thread-9
生产线程在执行,线程名称为:Thread-9
生产了3个馒头,现在的馒头数是:3
进入消费线程...Thread-35
消费线程在执行,线程名称为:Thread-35
消费了1个馒头,现在的馒头数是:2
消费线程被阻塞,线程名称为:之后:Thread-30
消费线程在执行,线程名称为:Thread-30
消费了1个馒头,现在的馒头数是:1
消费线程被阻塞,线程名称为:之后:Thread-36
消费线程在执行,线程名称为:Thread-36
消费了1个馒头,现在的馒头数是:0
消费线程被阻塞,线程名称为:之后:Thread-31
消费线程被阻塞,线程名称为:之前:Thread-31
消费线程被阻塞,线程名称为:之后:Thread-32
消费线程被阻塞,线程名称为:之前:Thread-32
进入消费线程...Thread-39
消费线程被阻塞,线程名称为:之前:Thread-39
进入消费线程...Thread-37
消费线程被阻塞,线程名称为:之前:Thread-37
进入消费线程...Thread-38
消费线程被阻塞,线程名称为:之前:Thread-38
生产线程被阻塞,线程名称为:之后:Thread-1
生产线程在执行,线程名称为:Thread-1
生产了3个馒头,现在的馒头数是:3
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
进入消费线程...Thread-33
消费线程在执行,线程名称为:Thread-33
消费了1个馒头,现在的馒头数是:2
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程被阻塞,线程名称为:之前:Thread-2
消费线程被阻塞,线程名称为:之后:Thread-38
消费线程在执行,线程名称为:Thread-38
消费了1个馒头,现在的馒头数是:1
消费线程被阻塞,线程名称为:之后:Thread-37
消费线程在执行,线程名称为:Thread-37
消费了1个馒头,现在的馒头数是:0
消费线程被阻塞,线程名称为:之后:Thread-39
消费线程被阻塞,线程名称为:之前:Thread-39
消费线程被阻塞,线程名称为:之后:Thread-32
消费线程被阻塞,线程名称为:之前:Thread-32
消费线程被阻塞,线程名称为:之后:Thread-31
消费线程被阻塞,线程名称为:之前:Thread-31
生产线程被阻塞,线程名称为:之后:Thread-2
生产线程在执行,线程名称为:Thread-2
生产了3个馒头,现在的馒头数是:3
消费线程被阻塞,线程名称为:之后:Thread-31
消费线程在执行,线程名称为:Thread-31
消费了1个馒头,现在的馒头数是:2
消费线程被阻塞,线程名称为:之后:Thread-32
消费线程在执行,线程名称为:Thread-32
消费了1个馒头,现在的馒头数是:1
消费线程被阻塞,线程名称为:之后:Thread-39
消费线程在执行,线程名称为:Thread-39
消费了1个馒头,现在的馒头数是:0
改成while之后,线程被吵醒之后从wait()之后执行,然后检查while循环体的条件,如果超过最大个数限制,则继续阻塞
二: 锁的重入性
在java内部,同一线程在调用自己类中其他synchronized方法/块或调用父类的synchronized方法/块都不会阻碍该线程的执行,就是说同一线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,也就是可以多次重入。
锁重入后的退出:
我们再来看看重入锁是怎么实现可重入性的,其实现方法是为每个锁关联一个线程持有者和计数器,当计数器为0时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为0,则释放该锁。
链接:http://www.tuicool.com/articles/Fr6FBnY
三 中断锁
Synchronized无法响应线程中断,ReentrantLock可以响应线程中断