1.synchronized关键字
synchronized是用来控制线程的并发执行的,它只能作用于一个方法或者一个代码块上,通过它能保证一段代码或一个方法有多个线程调用时能顺序执行。
工作机制:
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。释放锁是指持锁线程退出了synchronized同步方法或代码块。
示例代码:
package com.ajita;
public class TestSynchronized {
class Account {
private int total = 0;
public synchronized void add(int n) {
total = total + n;
}
public synchronized void minus(int n) {
total = total - n;
}
public int getTotal() {
return total;
}
}
class AddThread extends Thread {
private Account acct;
private int addCount = 0;
AddThread(String name, Account acct, int addNum) {
super(name);
this.acct = acct;
this.addCount = addNum;
}
public void run() {
for (int i = 0; i < addCount; i++) {
acct.add(1);
System.out.println(this.getName() + acct.getTotal());
try {
Thread.sleep(11);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class MinusThread extends Thread {
private Account acct;
private int minusNum = 0;
MinusThread(String name, Account acct, int minusNum) {
super(name);
this.acct = acct;
this.minusNum = minusNum;
}
public void run() {
for (int i = 0; i < minusNum; i++) {
acct.minus(1);
System.out.println(this.getName() + acct.getTotal());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void test() {
Account a = new Account();
System.out.println(a.getTotal());
Thread addThread = new AddThread("add", a, 50);
Thread minusThread = new MinusThread("minus", a, 50);
addThread.start();
minusThread.start();
try {
addThread.join();
minusThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(a.getTotal());
}
/**
* @param args
*/
public static void main(String[] args) {
TestSynchronized test = new TestSynchronized();
test.test();
}
}
使用synchronized锁,可能造成死锁的问题。最典型的是线程1先锁A,再来锁B,线程2先锁B,再来锁A,如下代码:
package com.ajita;
public class TestDeadLock {
public class DeadlockRisk {
private Object resourceA = new Object();
private Object resourceB = new Object();
public void lockAFirst() {
synchronized (resourceA) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (resourceB) {
System.out.println("lockAFirst get Locked");
}
}
}
public void lockBFirst() {
synchronized (resourceB) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (resourceA) {
System.out.println("lockBFirst get Locked");
}
}
}
}
class LockAFirstThread extends Thread {
public DeadlockRisk deadLock;
public void run() {
deadLock.lockAFirst();
}
}
class LockBFirstThread extends Thread {
public DeadlockRisk deadLock;
public void run() {
deadLock.lockBFirst();
}
}
public void test() {
DeadlockRisk lock = new DeadlockRisk();
LockAFirstThread a = new LockAFirstThread();
a.deadLock = lock;
LockBFirstThread b = new LockBFirstThread();
b.deadLock = lock;
a.start();
b.start();
}
/**
* @param args
*/
public static void main(String[] args) {
TestDeadLock test = new TestDeadLock();
test.test();
}
}
2.wait,notify,notifyAll
任意Java对象都有如下3个方法,不同线程之间可以利用同一个对象的这三个方法进行通信。
关于等待/通知,要记住的关键点是:
必须从同步环境内(就是synchronized 代码块内)调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法,除非它拥有那个对象的锁。
void notify()
唤醒在此对象监视器上等待的单个线程。 有重载的。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
void wait()
导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
最经典的例子就是生产者,消费者的例子。
代码如下,百度来的
package com.ajita;
/**
* Java线程:并发协作-生产者消费者模型
*
* @author leizhimin 2009-11-4 14:54:36
*/
public class ProducerAndConsumer {
public static void main(String[] args) {
Godown godown = new Godown(30);
Consumer c1 = new Consumer(50, godown);
Consumer c2 = new Consumer(20, godown);
Consumer c3 = new Consumer(30, godown);
Producer p1 = new Producer(10, godown);
Producer p2 = new Producer(10, godown);
Producer p3 = new Producer(10, godown);
Producer p4 = new Producer(10, godown);
Producer p5 = new Producer(10, godown);
Producer p6 = new Producer(10, godown);
Producer p7 = new Producer(80, godown);
c1.start();
c2.start();
c3.start();
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
p6.start();
p7.start();
}
}
/**
* 仓库
*/
class Godown {
public static final int max_size = 100; // 最大库存量
public int curnum; // 当前库存量
Godown() {
}
Godown(int curnum) {
this.curnum = curnum;
}
/**
* 生产指定数量的产品
*
* @param neednum
*/
public synchronized void produce(int neednum) {
// 测试是否需要生产
while (neednum + curnum > max_size) {
System.out.println("要生产的产品数量" + neednum + "超过剩余库存量"
+ (max_size - curnum) + ",暂时不能执行生产任务!");
try {
// 当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 满足生产条件,则进行生产,这里简单的更改当前库存量
curnum += neednum;
System.out.println("已经生产了" + neednum + "个产品,现仓储量为" + curnum);
// 唤醒在此对象监视器上等待的所有线程
notifyAll();
}
/**
* 消费指定数量的产品
*
* @param neednum
*/
public synchronized void consume(int neednum) {
// 测试是否可消费
while (curnum < neednum) {
try {
// 当前的生产线程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 满足消费条件,则进行消费,这里简单的更改当前库存量
curnum -= neednum;
System.out.println("已经消费了" + neednum + "个产品,现仓储量为" + curnum);
// 唤醒在此对象监视器上等待的所有线程
notifyAll();
}
}
/**
* 生产者
*/
class Producer extends Thread {
private int neednum; // 生产产品的数量
private Godown godown; // 仓库
Producer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
public void run() {
// 生产指定数量的产品
godown.produce(neednum);
}
}
/**
* 消费者
*/
class Consumer extends Thread {
private int neednum; // 生产产品的数量
private Godown godown; // 仓库
Consumer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
public void run() {
// 消费指定数量的产品
godown.consume(neednum);
}
}
本例仅仅是生产者消费者模型中最简单的一种表示,本例中,如果消费者消费的仓储量达不到满足,而又没有生产者,则程序会一直处于等待状态,这当然是不对的。实际上可以将此例进行修改,修改为,根据消费驱动生产,同时生产兼顾仓库,如果仓不满就生产,并对每次最大消费量做个限制,这样就不存在此问题了,当然这样的例子更复杂,更难以说明这样一个简单模型。