关键字:Synchronized、wait/notify、ReentrantLock、生产-消费者模型
这篇博文仅仅对java多线程的部分知识做个小结,欢迎大家补充。
Synchronized
说到多线程,大家(对于初学者来说)可能就会想到Synchronized这个关键字。Synchronized可以用在方法级别上,用来同步一个方法的访问,也可以同步一个代码块。形式如下:
// 同步方法
public synchronized void method(){
// 方法体
}
// 同步代码快
synchronized(object){
// 代码块
}
Synchronized 需要注意的是:Synchronized作用于方法时,Synchronized首先会对方法所属的对象加锁,对象的其他被Synchronized修饰的方法都不可以被其他线程访问,非Synchronized修饰发方法可以被多个线程同时访问。
wait/notify
wait/notify必须在Synchronized的作用范围内使用,也就是说wait/notify必须在同步方法体内或同步代码快内才有效,其他地方就会报IllegalMonitorStateException。
基于Synchronized、wait/notify机理实现生产-消费者模型
public class Basket {
private List<String> basket = new ArrayList<String>(10);
private boolean running = false ;
public void run(){
this.running = true;
}
public void stop(){
this.running = false;
}
public boolean isRunning(){
return running;
}
public void add(String e) {
synchronized (basket) {
while (basket.size() == 10) {
try {
basket.wait(); // 访问的线程会等待在此处,当再次被唤醒,从此处继续执行
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
basket.add(e);
String currentThreadName = Thread.currentThread().getName();
System.out.println("线程["+currentThreadName+"] "+e + " 生产一个产品");
basket.notifyAll();
}
}
public void remove(String msg) {
synchronized (basket) {
String currentThreadName = Thread.currentThread().getName();
while (basket.size() == 0) {
try {
System.out.println("线程["+currentThreadName+"] "+msg + ": please wait a little");
basket.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String result = basket.get(0);
basket.remove(0);
System.out.println("线程["+currentThreadName+"] "+msg + " 消费:" + result);
basket.notifyAll();
}
}
public void getResult() {
System.out.println(basket.toString());
}
}
生产者
class UProducer implements Runnable {
private String name = "";
private Basket basket;
public UProducer(String name, Basket basket) {
this.name = name;
this.basket = basket;
}
@Override
public void run() {
int i = 0;
while(basket.isRunning()){
basket.add(name + i++);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public String getName() {
return name;
}
}
消费者
class Customer implements Runnable {
private String name = "";
private Basket basket;
public Customer(String name, Basket basket) {
super();
this.name = name;
this.basket = basket;
}
@Override
public void run() {
int i = 0;
while(basket.isRunning()){
basket.remove(name + i++);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public String getName() {
return name;
}
}
测试代码
public static void main(String[] args) {
Basket basket = new Basket();
basket.run();
UProducer mrZhang = new UProducer("张三", basket);
UProducer mrLi = new UProducer("李四", basket);
Customer missWang = new Customer("王五", basket);
Customer missZhao = new Customer("赵六", basket);
new Thread(missWang, missWang.getName()).start();
new Thread(missZhao, missZhao.getName()).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(mrZhang, mrZhang.getName()).start();
new Thread(mrLi, mrLi.getName()).start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
basket.stop();
basket.getResult();
}
Synchronized关键字可以很方便的实现简单的多线程问题,jvm默认的为我们提供了对象的加锁、释放锁功能。Synchronized关键字同时存在一些问题。如一个线程得不到对象的锁会永远的等待。
ReentrantLock
ReentrantLock是对Synchronized的加强,提供了线程竞争锁的等待时间、多条件下的线程等待策略等等强大的功能。
具体的方法请参考API
以下是对生产-消费者模型的改写
class Basket {
private static final int CAPACITY = 10;
private List<Bread> container = new ArrayList<Bread>(CAPACITY);
private ReentrantLock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public void add(Bread bread) {
try {
lock.lockInterruptibly();
while (container.size() == CAPACITY) {
System.out.println("货源[" + Thread.currentThread().getName() + "]:no more ,i will call you if i need");
notFull.await();
}
container.add(bread);
System.out.println("货源[" + Thread.currentThread().getName() + "]:进货:" + bread);
notEmpty.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void remove() {
try {
lock.lockInterruptibly();
while (container.size() == 0) {
System.out.println("销售[" + Thread.currentThread().getName() + "]: please wait a little");
notEmpty.await();
}
Bread bread = container.get(0);
container.remove(0);
System.out.println("销售[" + Thread.currentThread().getName() + "]:销售:" + bread);
notFull.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class Bread {
private String name;
public Bread(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}