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) { ticketLock.lock();//获取锁 if(num>0) { try{ Thread.sleep(10); //输出卖票信息 System.out.println(Thread.currentThread().getName()+".....sale...."+num--); }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()+" 线程结束"); } }