参考:http://ifeve.com/java_lock_see2/
一:ticket自旋锁
- 上节讲了自选锁,自旋锁中常用的有三类,TicketLock ,CLHlock 和MCSlock,本节主要谈谈ticket自旋锁
- 概念:ticket锁也是自旋锁的一种,只是它是一种能保证顺序的自选锁,是公平锁
- 实现:
package com.eden.coreLearn.thread.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; /** * 排队自旋锁,公平锁 * * @author eden.ding1991@gmail.com 2016年7月14日 下午12:16:06 */ public class TicketSpinLock { //当期线程能占用的锁的序号 private AtomicInteger ticketNum = new AtomicInteger(0); //锁的序号 private AtomicInteger owner = new AtomicInteger(0); //当前线程持有的票据 private static final ThreadLocal<Integer> myTicketLocal = new ThreadLocal<Integer>(); public void lock() { int myTicket = ticketNum.getAndIncrement();//票据 myTicketLocal.set(myTicket); while (myTicket != owner.get()) { } } public void unlock() { int myTicket = myTicketLocal.get(); owner.compareAndSet(myTicket, myTicket + 1); } @Test public void testAdd() throws InterruptedException { AddClass ac = new AddClass(new TicketSpinLock()); for (int i = 0; i < 100; i++) { new Thread(new AddClassThread(ac), i + "").start(); } TimeUnit.SECONDS.sleep(20); } public class AddClassThread extends Thread { private AddClass addClass; public AddClassThread(AddClass addClass) { this.addClass = addClass; } @Override public void run() { addClass.add(); // addClass.add2(); } } public class AddClass { private int i; private TicketSpinLock ticketLock; public AddClass() { } public AddClass(TicketSpinLock ticketLock) { this.ticketLock = ticketLock; } public void add() { i = i + 1; System.out.println(String.format("i=%s", i)); } public void add2() { ticketLock.lock(); i = i + 1; System.out.println(String.format("i=%s", i)); ticketLock.unlock(); } } }
- 如上,主要通过三个变量实现ticket自选锁,ticketNum->表示想要占用的锁的序号,owner->表示当前锁的序号,myTicketLocal->记录每个线程想要持有的锁的序号,是ticketNum的线程备份;
- 第一个线程进来时,首先领一个号ticketNum,同时告诉这个号我已经领了,下个号(ticketNum)要+1;然后将自己的ticketNum存到自己的线程栈里,然后比较自己领的号是不是和当前锁的序号是一样的,如果一样获得锁,不一样等待锁,这个就是lock函数的过程
- 当一个线程释放锁时,首先取出当前自己占用锁的序号与当前正在被占用的锁的序号相比,如果相同,表是当前锁已经被使用,下一个锁应该要+1了,即owner+1
- 其实上述过程就相当于我们去银行办业务,然后在取票机取票排队等候是一样的,一个个人就是每个线程,而票号就是ticketNum,柜台显示器上显示的号码就是owner,只有票号ticketNum和柜台显示器上的号码owner相同时,你才能去柜台上办自己的业务,myTicketLocal变量的存在是因为在程序里ticketNum没有一个线程就会+1,所以我们必须将自己的ticketNum备份下来,这样才能在释放锁的时候与owner做比较
- 优缺点基本上也就是上节中提到的自旋锁的优缺点