package com.zejian.test;
/**
* @author zejian
* @time 2016年3月12日 下午2:55:42
* @decrition 模拟卖票线程
*/
public class Ticket implements Runnable
{
//当前拥有的票数
private int num = 100;
public void run()
{
while(true)
{
if(num>0)
{
try{Thread.sleep(10);}catch (InterruptedException e){}
//输出卖票信息
System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
}
}
}
}
上面是卖票线程类,下来再来看看执行类:
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月12日 下午2:54:18
* @decrition 模拟卖票系统,该案例只考虑单方面卖票,其他情况暂时不考虑
*/
public class TicketDemo {
public static void main(String[] args)
{
Ticket t = new Ticket();//创建一个线程任务对象。
//创建4个线程同时卖票
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
//启动线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行程序结果如下(仅截取部分数据):
方法 | 相关描述内容 |
void lock() | 获取锁,调用该方法当前线程会获取锁,当获取锁后。从该方法返回 |
void lockInterruptibly() throws InterruptedException |
可中断获取锁和lock()方法不同的是该方法会响应中断,即在获取锁 中可以中断当前线程。例如某个线程在等待一个锁的控制权的这段时 间需要中断。 |
boolean tryLock() | 尝试非阻塞获取锁,调用该方法后立即返回,如果能够获取锁则返回 true,否则返回false。 |
boolean tryLock(long time,TimeUnit unit) throws InterruptedException |
超时获取锁,当前线程在以下3种情况返回: 1.当前线程在超时时间内获取了锁 2.当前线程在超时时间被中断 3.当前线程超时时间结束,返回false |
void unlock() | 释放锁 |
Condition newCondition() | 条件对象,获取等待通知组件。该组件和当前的锁绑定,当前线程只有 获取了锁,才能调用该组件的await()方法,而调用后,当前线程将缩放 锁。 |
ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁
ReentrantLock lock = new ReentrantLock(true); //公平锁
lock.lock(); //如果被其它资源锁定,会在此等待锁释放,达到暂停的效果
try {
//操作
} finally {
lock.unlock(); //释放锁
}
2.防止重复执行代码:
ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) { //如果已经被lock,则立即返回false不会等待,达到忽略操作的效果
try {
//操作
} finally {
lock.unlock();
}
}
3.尝试等待执行的代码:
ReentrantLock lock = new ReentrantLock(true); //公平锁
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
//如果已经被lock,尝试等待5s,看是否可以获得锁,如果5s后仍然无法获得锁则返回false继续执行
try {
//操作
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace(); //当前线程被中断时(interrupt),会抛InterruptedException
}
这里有点需要特别注意的,把解锁操作放在finally代码块内这个十分重要。如果在临界区的代码抛出异常,锁必须被释放。否则,其他线程将永远阻塞。好了,ReentrantLock我们就简单介绍到这里,接下来我们通过
ReentrantLock来解决前面卖票线程的线程同步(安全)问题,代码如下:
package com.zejian.test;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zejian
* @time 2016年3月12日 下午2:55:42
* @decrition 模拟卖票线程
*/
public class Ticket implements Runnable {
//创建锁对象
private Lock ticketLock = new ReentrantLock();
//当前拥有的票数
private int num = 100;
public void run() {
while (true) {
try {
ticketLock.lock();//获取锁
if (num > 0) {
Thread.sleep(10);//输出卖票信息System.out.println(Thread.currentThread().getName()+".....sale...."+num--); }
} else {
break;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();//出现异常就中断
} finally {
ticketLock.unlock();//释放锁
}
}
}
}
public synchronized void method{
//method body
}
等价于
private Lock ticketLock = new ReentrantLock();
public void method{
ticketLock.lock();
try{
//.......
}finally{
ticketLock.unlock();
}
}
从这里可以看出使用synchronized关键字来编写代码要简洁得多了。当然,要理解这一代码,我们必须知道每个对象有一个内部锁,并且该锁有一个内部条件。由锁来管理那些试图进入synchronized方法的线程,由条件来管那些调用wait的线程(wait()/notifyAll/notify())。同时我们必须明白一旦有一个线程通过synchronied方法获取到内部锁,该类的所有
synchronied方法或者代码块都无法被其他线程访问直到当前线程释放了内部锁。刚才上面说的是同步方法,
synchronized还有一种同步代码块的实现方式:
Object obj = new Object();
synchronized(obj){
//需要同步的代码
}
其中obj是对象锁,可以是任意对象。那么我们就通过其中的一个方法来解决售票系统的线程同步问题:
class Ticket implements Runnable
{
private int num = 100;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
try{Thread.sleep(10);}catch (InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
}
}
}
}
}
//创建条件对象
Condition conditionObj=ticketLock.newCondition();
方法 | 函数方法对应的描述 |
void await() | 将该线程放到条件等待池中(对应wait()方法) |
void signalAll() | 解除该条件等待池中所有线程的阻塞状态(对应notifyAll()方法) |
void signal() | 从该条件的等待池中随机地选择一个线程,解除其阻塞状态(对应notify()方法) |
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月12日 下午10:44:25
* @decrition 烤鸭资源
*/
public class KaoYaResource {
private String name;
private int count = 1;//烤鸭的初始数量
private boolean flag = false;//判断是否有需要线程等待的标志
/**
* 生产烤鸭
*/
public synchronized void product(String name){
if(flag){
//此时有烤鸭,等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace()
;
}
}
this.name=name+count;//设置烤鸭的名称
count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;//有烤鸭后改变标志
notifyAll();//通知消费线程可以消费了
}
/**
* 消费烤鸭
*/
public synchronized void consume(){
if(flag){//如果没有烤鸭就等待
try{this.wait();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
flag = false;
notifyAll();//通知生产者生产烤鸭
}
}
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月12日 下午10:29:12
* @decrition 单生产者单消费者模式
*/
public class Single_Producer_Consumer {
public static void main(String[] args)
{
KaoYaResource r = new KaoYaResource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
//生产者线程
Thread t0 = new Thread(pro);
//消费者线程
Thread t2 = new Thread(con);
//启动线程
t0.start();
t2.start();
}
}
/**
* @author zejian
* @time 2016年3月12日 下午11:02:22
* @decrition 生产者线程
*/
class Producer implements Runnable
{
private KaoYaResource r;
Producer(KaoYaResource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.product("北京烤鸭");
}
}
}
/**
* @author zejian
* @time 2016年3月12日 下午11:02:05
* @decrition 消费者线程
*/
class Consumer implements Runnable
{
private KaoYaResource r;
Consumer(KaoYaResource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.consume();
}
}
}
在这个类中我们创建两个线程,一个是消费者线程,一个是生产者线程,我们分别开启这两个线程用于不断的生产消费,运行结果如下:
package com.zejian.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zejian
* @time 2016年3月13日 上午9:55:35
* @decrition 通过对象锁的方式来实现等待/通知机制
*/
public class KaoyaResourceByLock {
private String name;
private int count = 1;//烤鸭的初始数量
private boolean flag = false;//判断是否有需要线程等待的标志
//创建一个锁对象
private Lock resourceLock=new ReentrantLock();
//创建条件对象
private Condition condition= resourceLock.newCondition();
/**
* 生产烤鸭
*/
public void product(String name){
resourceLock.lock();//先获取锁
try{
if(flag){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name+count;//设置烤鸭的名称
count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;//有烤鸭后改变标志
condition.signalAll();//通知消费线程可以消费了
}finally{
resourceLock.unlock();
}
}
/**
* 消费烤鸭
*/
public void consume(){
resourceLock.lock();
try{
if(!flag){//如果没有烤鸭就等待
try{condition.await();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
flag = false;
condition.signalAll();//通知生产者生产烤鸭
}finally{
resourceLock.unlock();
}
}
}
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月13日 上午10:35:05
* @decrition 多生产者多消费者模式
*/
public class Mutil_Producer_Consumer {
public static void main(String[] args)
{
KaoYaResource r = new KaoYaResource();
Mutil_Producer pro = new Mutil_Producer(r);
Mutil_Consumer con = new Mutil_Consumer(r);
//生产者线程
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
//消费者线程
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
//启动线程
t0.start();
t1.start();
t2.start();
t3.start();
}
}
/**
* @author zejian
* @time 2016年3月12日 下午11:02:22
* @decrition 生产者线程
*/
class Mutil_Producer implements Runnable
{
private KaoYaResource r;
Mutil_Producer(KaoYaResource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.product("北京烤鸭");
}
}
}
/**
* @author zejian
* @time 2016年3月12日 下午11:02:05
* @decrition 消费者线程
*/
class Mutil_Consumer implements Runnable
{
private KaoYaResource r;
Mutil_Consumer(KaoYaResource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.consume();
}
}
}
就多了两条线程,我们运行代码看看,结果如下:
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月12日 下午10:44:25
* @decrition 烤鸭资源
*/
public class KaoYaResource {
private String name;
private int count = 1;//烤鸭的初始数量
private boolean flag = false;//判断是否有需要线程等待的标志
/**
* 生产烤鸭
*/
public synchronized void product(String name){
if(flag){
//此时有烤鸭,等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name+count;//设置烤鸭的名称
count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;//有烤鸭后改变标志
notifyAll();//通知消费线程可以消费了
}
/**
* 消费烤鸭
*/
public synchronized void consume(){
if(!flag){//如果没有烤鸭就等待
try{this.wait();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
flag = false;
notifyAll();//通知生产者生产烤鸭
}
}
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月12日 下午10:44:25
* @decrition 烤鸭资源
*/
public class KaoYaResource {
private String name;
private int count = 1;//烤鸭的初始数量
private boolean flag = false;//判断是否有需要线程等待的标志
/**
* 生产烤鸭
*/
public synchronized void product(String name){
while(flag){
//此时有烤鸭,等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name+count;//设置烤鸭的名称
count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;//有烤鸭后改变标志
notifyAll();//通知消费线程可以消费了
}
/**
* 消费烤鸭
*/
public synchronized void consume(){
while(!flag){//如果没有烤鸭就等待
try{this.wait();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);//消费烤鸭1
flag = false;
notifyAll();//通知生产者生产烤鸭
}
}
运行代码,结果如下:
package com.zejian.test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author zejian
* @time 2016年3月13日 下午12:03:27
* @decrition 通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
*/
public class ResourceBy2Condition {
private String name;
private int count = 1;
private boolean flag = false;
//创建一个锁对象。
Lock lock = new ReentrantLock();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition producer_con = lock.newCondition();
Condition consumer_con = lock.newCondition();
/**
* 生产
* @param name
*/
public void product(String name)
{
lock.lock();
try
{
while(flag){
try{producer_con.await();}catch(InterruptedException e){}
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name);
flag = true;
// notifyAll();
// con.signalAll();
consumer_con.signal();//直接唤醒消费线程
}
finally
{
lock.unlock();
}
}
/**
* 消费
*/
public void consume()
{
lock.lock();
try
{
while(!flag){
try{consumer_con.await();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName()+"...消费者.5.0......."+this.name);//消费烤鸭1
flag = false;
// notifyAll();
// con.signalAll();
producer_con.signal();//直接唤醒生产线程
}
finally
{
lock.unlock();
}
}
}
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月13日 下午2:45:52
* @decrition 死锁示例
*/
public class DeadLockDemo {
private static String A="A";
private static String B="B";
public static void main(String[] args) {
DeadLockDemo deadLock=new DeadLockDemo();
while(true){
deadLock.deadLock();
}
}
private void deadLock(){
Thread t1=new Thread(new Runnable(){
@SuppressWarnings("static-access")
@Override
public void run() {
synchronized (A) {
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized(B){
System.out.println("1");
}
}
});
Thread t2 =new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println("2");
}
}
}
});
//启动线程
t1.start();
t2.start();
}
}
package com.zejian.test;
/**
* @author zejian
* @time 2016年3月13日 下午4:10:03
* @decrition join案例
*/
public class JoinDemo {
public static void main(String[] args) {
Thread previous = Thread.currentThread();
for(int i=0;i<10;i++){
//每个线程拥有前一个线程的引用。需要等待前一个线程终止,才能从等待中返回
Thread thread=new Thread(new Domino(previous),String.valueOf(i));
thread.start();
previous=thread;
}
System.out.println(Thread.currentThread().getName()+" 线程结束");
}
}
class Domino implements Runnable{
private Thread thread;
public Domino(Thread thread){
this.thread=thread;
}
@Override
public void run() {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 线程结束");
}
}