java线程安全队列以及lock、tryLock和lockInterruptibly的差別

Java提供的线程安全的Queue可以分为阻 塞队列非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。
线程安全就是说多线程访问同一代码,不会产生不确定的结果
1、BlockingQueue提供的常用方法:

可能报异常
返回布尔值
可能阻塞
设定等待时间
 
入队
add(e)
offer(e)
put(e)
offer(e, timeout, unit)
出队
remove()
poll()
take()
poll(timeout, unit)
查看
element()
peek()
1)BlockingQueue 注意事项:
  • 可以是限定容量的:在任意时间都可以有一个remainingCapacity,超出此容量,便无法无阻塞地put 附加元素。没有容量约束的BlockingQueue 默认Integer.MAX_VALUE 的剩余容量。
  • BlockingQueue 支持Collection 接口:使用remove(x) 从队列中移除任意一个元素是有可能的。比如:在取消排队信息时。
  • BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁或其他形式的并发控制来自动达到线程安全目的
public boolean offer(E e) {       
        if (e == null) throw new NullPointerException();       
        final ReentrantLock lock = this.lock;//每个对象对应一个显示的锁       
        lock.lock();//请求锁直到获得锁(不可以被interrupte)       
        try {       
            if (count == items.length)//如果队列已经满了       
                return false;       
            else {       
                insert(e);       
                return true;       
            }       
        } finally {       
            lock.unlock();//       
        }       
} 


我们可以看到,先不考虑阻塞非阻塞,队列的每个方法都带有锁,可以知道是线程安全的。
2)BlockingQueue 接口具体实现:
  • ArrayBlockingQueue:由数组实现的有界阻塞队列,其中元素以FIFO(先入先出)顺序排序,其构造函数必须带一个int参数来指明其大小,可选参数fair(布尔)决定在多线程抢占时是否使用公平锁;
  • LinkedBlockingQueue:由链表实现的阻塞队列,其中元素以FIFO(先入先出)顺序排序,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定;
  • PriorityBlockingQueue:无界的阻塞队列,其所含对象的排序不是FIFO而是依据对象的自然排序顺序或者是构造函数的Comparator决定的顺序。
  • SynchronousQueue:特殊的BlockingQueue,对其的操作必须是放和取交替完成的。
2、ConcurrentLinkedQueue:
基于链接节点的、无界的、非阻塞的、线程安全的队列,其中元素按照 FIFO(先进先出)原则排序,它利用了cas实现了非阻塞算法。有几点需要注意:
1)ConcurrentLinkedQueue的size()是要遍历一遍集合的,所以尽量要避免用size而改用isEmpty(),以免性能过慢。;
2)在使用ConcurrentLinkedQueue时要注意,如果直接使用它提供的函数,比如add或者poll方法,这样我们自己不需要做任何同步;但如果是非原子操作,
if(!queue.isEmpty()) {
	queue.poll(obj);
}
我们很难保证,在调用了isEmpty()之后,poll()之前,这个queue没有被其他线程修改。所以对于这种情况,我们还是需要自己同步: 
synchronized(queue) {
	if(!queue.isEmpty()) {
		queue.poll(obj);
	}
}
3、lock、tryLock和lockInterruptibly的差別:
1)java中ReentrantLock下有三个获取所得方法:
  1. lock():若lock被thread A取得,thread B会进入block狀態,直到取得lock;
  2. tryLock():若当下不能取得lock,thread就会放弃,可以设置一个超时时间参数,等待多久获取不到锁就放弃;
  3. lockInterruptibly():跟lock()情況一下,但是thread B可以通过interrupt被唤醒处理InterruptedException异常。
2)lock示例:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo {
	private ReentrantLock	lock = new ReentrantLock();
	
	public void doSomeThing(Thread thread) {
		lock.lock();
         try {
        	System.out.println(thread.getName() + "得到了锁.");
                Thread.sleep(1000);
          } catch (Exception e) {
            e.printStackTrace();
          } finally {
            System.out.println(thread.getName() + "释放了锁.");
            lock.unlock();
         }
	}
	
	public static void main(String[] args) throws InterruptedException {
		final LockDemo lockDemo = new LockDemo();
		
		Thread threada = new Thread("Thread A") {
            @Override
            public void run() {
            	lockDemo.doSomeThing3(Thread.currentThread());
            }
  };
		threada.start();
        
        	Thread threadb = new Thread("Thread B") {
            @Override
            public void run() {
            	lockDemo.doSomeThing3(Thread.currentThread());
            }
   };
		threadb.start();
	}
}
输出:
Thread A得到了锁.
Thread A释放了锁.
Thread B得到了锁.
Thread B释放了锁.

3)trylock示例:
public void doSomeThing1(Thread thread) {
	if (lock.tryLock()) {
            try {
                   System.out.println(thread.getName() + "得到了锁.");
                   Thread.sleep(1000);
             } catch (Exception e) {
                   e.printStackTrace();
              } finally {
                   System.out.println(thread.getName() + "释放了锁.");
                   lock.unlock();
             }
        } else {
               System.out.println(thread.getName() + "获取锁失败.");
        }
}
输出:
Thread A得到了锁.
Thread B获取锁失败.
Thread A释放了锁.

4)lockInterruptibly示例:
public void doSomeThing3(Thread thread) {
		try {
			lock.lockInterruptibly();
			System.out.println(thread.getName() + " 得到了锁.");
			Thread.sleep(6000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
			System.out.println(thread.getName() + " interrupted.");
		} finally {
			System.out.println(thread.getName() + " 释放了锁.");
			lock.unlock();
        }
}
public static void main(String[] args) throws InterruptedException {
	final LockDemo lockDemo = new LockDemo();
		
	Thread threada = new Thread("Thread A") {
       		@Override
            	public void run() {
            		lockDemo.doSomeThing1(Thread.currentThread());
            	}
        };
	threada.start();
        
        Thread threadb = new Thread("Thread B") {
            @Override
            public void run() {
            	lockDemo.doSomeThing1(Thread.currentThread());
            }
        };
	threadb.start();
	threadb.interrupt();
}
输出:
Thread A 得到了锁.
java.lang.InterruptedException
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1219)
	at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
	at LockDemo.doSomeThing3(LockDemo.java:60)
	at LockDemo$2.run(LockDemo.java:86)
Thread B interrupted.
Thread B 释放了锁.
Exception in thread "Thread B" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
	at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)
	at LockDemo.doSomeThing3(LockDemo.java:68)
	at LockDemo$2.run(LockDemo.java:86)
Thread A 释放了锁.


猜你喜欢

转载自blog.csdn.net/liuxiao723846/article/details/80436208