Java 并发编程 同步控制 三
来源:互联网 发布:中国移动 plmn 网络id 编辑:程序博客网 时间:2024/06/08 08:25
1、 重入锁 ReentrantLock
synchronized 的重入锁: ReentrantLock。 重入锁需要手动进行加锁和解锁: lock 和unlock。
public class ReentrantLockTest implements Runnable{ public static ReentrantLock lock = new ReentrantLock(); public static int i=0; public void run() { for(int j=0;j<10000;j++){ lock.lock(); try{ i++; }finally{ lock.unlock(); } } } public static void main(String args[]){ ReentrantLockTest test = new ReentrantLockTest(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); Thread t3 = new Thread(test); t3.start(); t1.start(); t2.start(); try { t1.join(); t2.join(); t3.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(i); }}
重入锁相对于 synchronized 还可以相应中断。
public class IntLock implements Runnable { public static ReentrantLock lock1 = new ReentrantLock(); public static ReentrantLock lock2 = new ReentrantLock(); int lock;//线程的私有的属性,每个线程都有每个线程的值, 这个值线程安全吗, // 如果是Static,那么每个对象的值都一样了。 public IntLock(int lock) { this.lock = lock; } public void run() { try { System.out.println(Thread.currentThread().getId()+ ">>>"+lock); if (lock == 1) { lock1.lockInterruptibly(); try{ Thread.sleep(500); }catch(InterruptedException e){} lock2.lockInterruptibly(); System.out.println(Thread.currentThread().getId()+"执行"); System.out.println(Thread.currentThread().getId()+ "执行>>>"+lock); }else{ lock2.lockInterruptibly(); System.out.println(Thread.currentThread().getId()+ ">>>"+lock); //lock2.lock(); try{ Thread.sleep(500); }catch(InterruptedException e){} //lock1.lock(); lock1.lockInterruptibly(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ if(lock1.isHeldByCurrentThread()){ lock1.unlock(); } if(lock2.isHeldByCurrentThread()){ lock2.unlock(); } System.out.println(Thread.currentThread().getId()+":线程退出"); } } public static void main(String args[]) throws InterruptedException{ IntLock r1 = new IntLock(1); IntLock r2 = new IntLock(2); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); Thread.sleep(1000); t2.interrupt(); }
线程T1 和T2 启动后,t1先占用locl1,再占用lock2; T2正好相反。 因此很容易形成t1和t2 之间的互相等待。 在这里,对锁的请求,统一使用lockInterruptibly() 方法,这是一个可以对中断进行响应的锁申请动作,即在等待锁的过程中,可以响应中断。
锁申请等待限时
lock.tryLock(5,TimeUnit.SECONDS) 可以在锁请求中,设置等待的时间。
tryLock 方法也可以不带参数直接运行。在这种情况下,当前线程会尝试获得锁,如果锁并未并其他线程占用,则申请锁会成功,并立即放回true。 如果锁别其他线程占用,则当前线程不会进行等待,而是立即返回false。这种模式不会引起线程等待,因此也不会产生死锁。
重入锁还有一个锁类型就是可以设置公平锁,默认情况是非公平锁。
构造函数为
public ReentrantLock(boolean fair)
设置为true就是公平锁。 实现公平锁要求系统维护一个有序队列,以保证每个线程按照顺序进行获得锁。
ReetrantLock几个重要方法如下:
lock :获得重入锁,如果锁已经被占用,则等待。
lockInterruptibly 获得锁,但优先响应中断。
trylock 尝试获得锁,如果成功返回true,失败返回flase。该方法不等待,立即返回。
tryLock(long time,TimeUnit unit) 在给定时间内尝试获得锁
unlock 释放锁
在重入锁的实现中,主要包含三个要素:
第一个 :原子状态。 原子状态使用CAS来存储当前锁的状态。判断锁是否已经被别的线程持有。
第二个: 等待队列,所有没有请求到锁的线程,会进入等待队列进行等待。待有线程释放锁后,系统就能从等待队列中唤醒一个线程,继续工作。
第三: 阻塞原语;park 和unpark ,用来挂起和恢复线程。没有得到锁的线程将会被挂起。
2、Condition条件
Condition 和wait和notify的方法的作用大致相同。 Condition.newCondition 方法可以生成一个与当前重入锁绑定的Condition实例。
Condition接口方法如下:
await() throws InterruptedException;// 会使当前线程等待,同时释放当前锁,当其他线程中使用signal 或者 signalAll方法时,线程会重新获得锁并继续执行。或者当前线程被中断时,也能跳出等待。awaitUninterruptibly() // 此方法 不会在等待过程中相应中断awaitNanos(long nanosTimeout) throws InterruptedExcetion;boolean await (long time,TimeUnit unit)throws InterruptedException;boolean awaitUntil(Date deadline) throws IntertupredException;void signal()// 唤醒一个在等待中的线程。void signalAll();
jdk中的阻塞队列就是通过 Condition实现的。
BlockingQueue:阻塞队列
ArrayBlockingQueue 是基于数组实现的,适合做有界队列。
LIinkedBlockingQueue 基于链表 适合做无界队列
BlockingQueue之所以适合作为数据共享的通道,关键在于Blocking上。 当服务线程,处理完成队列中所有消息后,它如何知道下一条信息何时到来呢?
BlockingQueue很好的解决了这个问题,它让服务线程在队列为空时,进行等待,当有新的消息进入队列后,自动将线程唤醒。
那么它是如何实现呢? 以ArrayBlockingQueue为例,来一探究竟。
ArrayBlockingQueue的内部元素都放置在一个对象数组中:
final Object [] items;
向队列中压入元素可以使用offer 和put方法。对于offer方法,如果当前队列已经满了,它就立即返回false。 如果没有满,则执行正常的入队操作。所以我们不讨论这个方法。我们需要关注的是put方法,put方法也是将元素压入队列队尾。但如果队列满了,他会一直等待,直到队列中有空闲的位置。
从队列中弹出元素可以使用poll方法和take方法。 他们都可以从队列的头部获得一个元素,不同之处在于:如果队列为空,poll方法直接返回null。而take方法会等待,直到队列内有可用元素。
因此,put方法和take 方法才是体现blocking的关键。为了做好等待和通知两件事,在ArrayBlockingQueue内部定义了以下一些字段:
final ReentrantLock lock;pirvate final Condition notEmpty;private final Condition notFull;
当执行take操作时,如果队列为空,则当前线程等待在notEmpty 上,新元素入队时,则进行一次notEmpty上的通知。
public E take() throws InterruptedException{ final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try{ while(count==0) notEmpty.await(); return extract(); }finally{ lock.unlock(); } }
private void insert(E e){ items[putIndex]=x; putIndex=inc(putIndex); ++count; notEmpty.signal();//发送一个队列不空的信号,此时take方法被唤醒。 }
同理,对于put操作,也是一样,当队列满时,需要让压入线程等待。
public void put(E e) throws InterruptedException{ checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try{ while(count==items.length) notFull.await(); insert(e) }finally{ lock.unlock(); } }
当有元素从队列中被挪走,队列中出现空位时, 自然也需要通知等待入队的线程:
private E extract() { final Object[] items = this.items; E x = this.<E>cast(items[takeIndex]); items[takeIndex] = null; takeIndex = inc(takeIndex); --count; notFull.signal();//发送一个不队列未满的信号 return x; }
3、允许多个线程同时访问:信号量 Semaphore
信号量为多线程协作提供了更为强大的控制方法。 广义上说,信号量是对锁的扩展。无论是内部锁synchronized还是重入锁ReentrantLock,一次都只能允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。
构造函数如下
public Semaphore(int permits) public Semaphore(int permits, boolean fair)
在构造信号量对象时,必须指定信号量的准入数,即同时能申请多少个许可。当每个线程只申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一个资源。信号量的主要逻辑方法有:
public void acquire() //尝试获得一个准入的许可。 若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。acquireUninterruptibly // 方法和acquire方法类似,但是不响应中断。tryAcquire//尝试获得一个许可,如果成功返回true,如果失败返回false,它不会进行等待。release //用于在线程访问资源结束后,释放一个许可,以使其它等待许可的线程可以进入资源的访问。
public class SemaphoreTest implements Runnable{ final Semaphore semp = new Semaphore(5); public void run() { try { semp.acquire(); Thread.sleep(2000); System.out.println(Thread.currentThread().getId()+": done!"); semp.release(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String args[]){ ExecutorService exec = Executors.newFixedThreadPool(20); final SemaphoreTest demo = new SemaphoreTest(); for(int i=0;i<20;i++){ exec.submit(demo); } exec.shutdown(); }}
- Java 并发编程 同步控制 三
- JAVA并发编程(三)——同步控制(下)
- Java并发编程(三)同步
- JAVA并发编程(二)——同步控制(上)
- java并发编程(三)----线程的同步
- java并发(三、同步)
- Java并发编程(6)-同步
- Java并发编程2-同步
- Java并发编程 同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- Java并发编程:同步容器
- c++ assert() 使用方法
- select的fd超过1024将会非常危险------FD_SET导致core dump
- <Python learning>Basic
- 使用Java实现短信发送
- ActiveMQ性能测试
- Java 并发编程 同步控制 三
- 用U盘安装ubuntu系统
- SiteMesh3使用。注解方式
- JavaEE目录
- (int&)a和(int)a的区别
- 英语魔法师之语法俱乐部阅读笔记
- 23种设计模式学习系列---概括
- caffe可视化方法(21天实战caffe)python版
- hdu2699