• 关于 线程让步 的搜索结果

问题

java中线程同步,线程让步,线程休眠的区别和联系是什么

蛮大人123 2019-12-01 19:29:33 867 浏览量 回答数 1

回答

线程同步:是保证多线程安全访问竞争资源的一种手段,java中常用的是加锁机制即synchronized同步代码块实现的。线程让步的方法是yield(),休眠方法是sleep()。sleep()方法和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程。两者的区别在于:sleep()方法会给其他线程运行的机会,不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。此外,当线程执行了sleep(long millis)方法,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法,将转到就绪状态。sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常。线程的本质是一个轻量级的进程,这个是java 底层实现的,都是本地实现的native方法,我们使用线程时直接通过Thread类开发多线程应用就行了。

蛮大人123 2019-12-02 02:23:13 0 浏览量 回答数 0

回答

一.Lock接口(java.util.concurrent.locks): void lock():获取锁,阻塞方式;如果资源已被其他线程锁定,那么lock将会阻塞直到获取锁,锁阻塞期间不受线程的Interrupt的影响,在获取锁成功后,才会检测线程的interrupt状态,如果interrupt=true,则抛出异常。 unlock():释放锁 tryLock():尝试获取锁,并发环境中"闯入"行为,如果有锁可用,直接获取锁并返回true,否则范围false. lockInterruptibly():尝试获取锁,并支持"中断"请求。与lock的区别时,此方法的开始、结束和执行过程中,都会不断检测线程的interrupt状态,如果线程被中断,则立即抛出异常;而不像lock方法那样只会在获取锁之后才检测。 二.Lock接口实现类 Lock直接实现,只有3个类:ReentrantLock和WriteLock/ReadLock;这三种锁;Lock和java的synchronized(内置锁)的功能一致,均为排他锁. ReentrantLock为重入排他锁,对于同一线程,如果它已经持有了锁,那么将不会再次获取锁,而直接可以使用. ReentrantReadWriteLock并没有继承ReentrantLock,而是一个基于Lock接口的单独实现.它实现了 ReadWriteLock,即读写分离锁,是一种采用锁分离技巧的API. 尽管在API级别ReentrantReadWriteLock和ReentrantLock没有直接继承关系,但是ReentrantReadWriteLock中的ReadLock和WriteLock都具有ReentrantLock的全部语义(简单说,就是把ReentrantLock的代码copy了一下.),即锁的可重入性.WriteLock支持Condition(条件),ReadLock不支持. Lock的实现类中,都包含了2中锁等待策略:公平和非公平;其实他们的实现也非常简单,底层都是使用了queue来维持锁请求顺序.[参考:http://shift-alt-ctrl.iteye.com/blog/1839142] 公平锁,就是任何锁请求,首先将请求加入队列,然后再有队列机制来决定,是阻塞还是分配锁. 非公平,就是允许"闯入",当然公平锁,也无法干扰"闯入",对于任何锁请求,首先检测锁状态是否可用,如果可用直接获取,否则加入队列.. ReentrantLock本质上和synchronized修饰词是同一语义,如果一个线程lock()之后,其他线程进行lock时必须阻塞,直到当前线程的前续线程unlock.[执行lock操作时,将会被队列化(假如在公平模式下),获取lock的线程都将具有前续/后继线程,前续线程就是当前线程之前执行lock操作而阻塞的线程,后继线程就是当前线程之后执行lock操作的线程;那么对于unlock操作就是"解锁"信号的传递,如果当前线程unlock,那么将会触发后继线程被"唤醒",即它因为lock操作阻塞状态被解除.];这是ReentrantLock的基本原理,但是当ReentrantLock在Conditon情况下,事情就变得更加复杂.[参加下述] 三.Condition:锁条件 Condition与Lock形成happen-before关系。Condition将Object的监视器方法(wait,notify,notifyAll)分解成截然不同的对象,以便通过这些对象与任意Lock实现组合。使Lock具有等待“集合”的特性,或者“类型”;Lock替代了synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。(synchronized + object.wait对应Lock + Condition.await) Condition又称条件队列,为线程提供了一个含义,以便在某种状态条件现在可能为true的其他线程通知它之前,一直挂起该线程。即多个线程,其中一个线程因为某个条件而阻塞,其他线程当“条件”满足时,则“通知”哪些阻塞的线程。这,几乎和object中wait和notify的机制一样。 Condition和wait一样,阻塞时也将原子性的释放锁(间接执行了release()方法)。并挂起线程。Condition必须与Lock形成关系,只有获取lock权限的,才能进行Condition操作。Condition底层基于AQS实现,条件阻塞,将以队列的方式,LockSupport支持。其实现类有ConditionObject,这也是Lock.newCondition()的返回实际类型,在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。 void await() throws InterruptedException:当前线程阻塞,并原子性释放对象锁。如下条件将触发线程唤醒: 当线程被中断(支持中断响应), 其他线程通过condition.signal()方法,且碰巧选中当前线程唤醒 其他线程通过condition.signalAll()方法 发生虚假唤醒 底层实现,await()方法将当前线程信息添加到Conditon内部维护的"await"线程队列的尾部(此队列的目的就是为singal方法保持亟待唤醒的线程的顺序),然后释放锁(执行tryRelease()方法,注意此处释放锁,仅仅是释放了锁信号,并不是unlock,此时其他线程仍不能获取锁--lock方法阻塞),然后使用LockSupport.park(this)来强制剥夺当前线程执行权限。await方法会校验线程的中断标记。 由此可见,await()方法执行之后,因为已经"归还"了锁信号,那么其他线程此时执行lock方法,将不再阻塞.. void awaitUninterruptibly():阻塞,直到被唤醒。此方法不响应线程中断请求。即当线程被中断时,它将继续等待,直到接收到signal信号(你应该能想到"陷阱"),当最终从此方法返回时,仍然将设置其中断状态。 void signal()/signalAll():唤醒一个/全部await的线程。 对于signal()方法而言,底层实现为,遍历await"线程队列,找出此condition上最先阻塞的线程,并将此阻塞线程unpark.至此为止,我们似乎发现"锁信号"丢失了,因为在线程await时通过tryRelease时释放了一次信号.那么被signal成功的线程,首先执行一次acquire(增加锁信号),然后校验自己是否被interrupted,如果锁信号获取成功且线程状态正常,此时才正常的从await()方法退出.经过这么复杂的分析,终于明白了ReentrantLock + Condition情况下,锁状态变更和线程控制的来龙去脉... Java代码 收藏代码 //////例子: private Lock lock = new ReentrantLock(); private Condition full = lock.newCondition(); private Condition empty = lock.newCondition(); public Object take(){ lock.lock(); try{ while(isEmpty()){ empty.await() } Object o = get() full.signalAll(); return o; }finally{ lock.unlock(); } } public void put(Object o){ lock.lock(); try{ while(isFull()){ full.await(); } put(o); empty.signalAll(); }finally{ lock.unlock(); } } 四.机制 Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。注意,Lock 实例只是普通的对象,其本身可以在 synchronized 语句中作为目标使用。获取 Lock 实例的监视器锁与调用该实例的任何 lock() 方法没有特别的关系。为了避免混淆,建议除了在其自身的实现中之外,决不要以这种方式使用 Lock 实例。 Lock接口具有的方法: void lock():获取锁,阻塞直到获取。 void lockInterruptibly() throws InterrutedException:获取锁,阻塞直到获取成功,支持中断响应。 boolean tryLock():尝试获取锁,返回是否获取的结果。如果碰巧获取成功,则返回true,此时已经持有锁。 boolean tryLock(long time,TimeUnit) throws InterruptedException:尝试获取锁,获取成功返回true,超时时且没有获取锁则返回false。 void unlock():释放锁。约定只有持有锁者才能释放锁,否则抛出异常。 void newCondition():返回绑定到lock的条件。 五.ReadWriteLock ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer(写锁),读取锁可以由多个 reader 线程同时保持(共享锁)。写入锁是独占的。所有 ReadWriteLock 实现都必须保证 writeLock 操作的内存同步效果也要保持与相关 readLock 的联系。也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。 与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。 Lock readLock():返回读锁。 Lock writeLock():返回写锁。 六.ReentrantLock ReentrantLock,重入排它锁,它和synchronized具有相同的语义以及在监视器上具有相同的行为,但是功能更加强大。 ReetrantLock将由最近成功获得锁且还没有释放锁的线程标记为“锁占有者”;当锁没有被线程持有时,调用lock方法将会成功获取锁并返回,如果当前线程为锁持有者,再次调用lock将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。 ReentrantLock的构造方法,允许接收一个“公平策略”参数,“公平策略”下,多个线程竞争获取锁时,将会以队列化锁请求者,并将锁授予队列的head。在“非公平策略”下,则不完全保证锁获取的顺序,允许闯入行为(tryLock)。 ReentrantLock基于AQS机制,锁信号量为1,如果信号量为1且当前锁持有者不为自己,则不能获取锁。释放锁时,如果当前锁持有者不是自己,也将抛出“IllegalMonitorStateException”。由此可见,对于ReentrantLock,lock和release方法是需要组合出现。 七.ReentrantReadWriteLock:可重入读写分离锁 重入性 :当前线程可以重新获取相应的“读锁”或者“写锁”,在写入线程保持的所有写入锁都已经释放后,才允许重入reader(读取线程)使用它们。writer线程可以获取读锁,但是reader线程却不能直接获取写锁。 锁降级:重入还允许写入锁降级为读锁,其实现方式为:先获取写入锁,然后获取读取锁,最后释放写入锁。但是读取锁不能升级为写入锁。 Conditon的支持:只有写入锁支持conditon,对于读取锁,newConditon方法直接抛出UnsupportedOperationException。 ReentrantReadWriteLock目前在java api中无直接使用。ReentrantReadWriteLock并没有继承自 ReentrantLock,而是单独重新实现。其内部仍然支持“公平性”“非公平性”策略。 ReentrantReadWriteLock基于AQS,但是AQS只有一个state来表示锁的状态,所以如果一个state表示2种类型的锁状态,它做了一个很简单的策略,“位运算”,将一个int类型的state拆分为2个16位段,左端表示readlock锁引用计数,右端16位表示write锁。在readLock、writeLock进行获取锁或者释放锁时,均是通过有效的位运算和位控制,来达到预期的效果。 八.ReadLock void lock():获取读取锁,伪代码如下: Java代码 收藏代码 //如果当前已经有“写锁”,且持有写锁者不是当前线程(如果是当前线程,则支持写锁,降级为读锁),则获取锁失败 //即任何读锁的获取,必须等待队列中的写锁释放 //c为实际锁引用量(exclusiveCount方法实现为:c & ((1<<16) -1) if (exclusiveCount(c) != 0 &&getExclusiveOwnerThread() != current) return -1; //CAS操作,操作state的左端16位。 if(CAS(c,c + (1<<16))){ return 1; } void unlock():释放read锁,即共享锁,伪代码如下: Java代码 收藏代码 //CAS锁引用 for (;;) { int c = getState(); int nextc = c - (1<<16);//位操作,释放一个锁。 if (compareAndSetState(c, nextc)) return nextc == 0; } 九.WriteLock void lock():获取写入锁,伪代码如下: Java代码 收藏代码 //当前线程 Thread current = Thread.currentThread(); //实际的锁引用state int c = getState(); //右端16位,通过位运算获取“写入锁”的state int w = exclusiveCount(c); //如果有锁引用 if (c != 0) { //且所引用不是自己 if (w == 0 || current != getExclusiveOwnerThread()){ return false; } } //如果写入锁state为0,且CAS成功,则设置state和独占线程信息 if ((w == 0 && writerShouldBlock(current)) ||!compareAndSetState(c, c + acquires)){ return false; } setExclusiveOwnerThread(current); return true; void unlock():释放写入锁,伪代码如下: Java代码 收藏代码 //计算释放锁的信号量 int nextc = getState() - releases; //对于写入锁,则校验当前线程是否为锁持有者,否则不可以释放(死锁) if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); //释放锁,且重置独占线程信息 if (exclusiveCount(nextc) == 0) { setExclusiveOwnerThread(null); setState(nextc); return true; } else { setState(nextc); return false; } 十.LockSupport:用来创建锁和其他同步类的基本线程阻塞原语。 底层基于hotspot的实现unsafe。park 和 unpark 方法提供了阻塞和解除阻塞线程的有效方法。三种形式的 park(即park,parkNanos(Object blocker,long nanos),parkUntil(Object blocker,long timestamp)) 还各自支持一个 blocker 对象参数。此对象在线程受阻塞时被记录,以允许监视工具和诊断工具确定线程受阻塞的原因。(这样的工具可以使用方法 getBlocker(java.lang.Thread) 访问 blocker。)建议最好使用这些形式,而不是不带此参数的原始形式。 在锁实现中提供的作为 blocker 的普通参数是 this。 static void park(Object blocker):阻塞当前线程,直到如下情况发生: 其他线程,调用unpark方法,并将此线程作为目标而唤醒 其他线程中断当前线程此方法不报告,此线程是何种原因被放回,需要调用者重新检测,而且此方法也经常在while循环中执行 Java代码 收藏代码 while(//condition,such as:queue.isEmpty){ LockSupport.park(queue);//此时queue对象作为“阻塞”点传入,以便其他监控工具查看,queue的状态 //检测当前线程是否已经中断。 if(Thread.interrupted()){ break; } } void getBlocker(Thread t):返回提供最近一次尚未解除阻塞的park的阻塞点。可以返回null。 void unpark(Thread t):解除指定线程阻塞,使其可用。参数null则无效果。 LockSupport实例(不过不建议在实际代码中直接使用LockSupport,很多时候,你可以使用锁来控制): Java代码 收藏代码 /////////////Demo public class LockSupportTestMain { /** * @param args */ public static void main(String[] args) throws Exception{ System.out.println("Hear!"); BlockerObject blocker = new BlockerObject(); LThread tp = new LThread(blocker, false); LThread tt = new LThread(blocker, true); tp.start(); tt.start(); Thread.sleep(1000); } static class LThread extends Thread{ private BlockerObject blocker; boolean take; LThread(BlockerObject blocker,boolean take){ this.blocker = blocker; this.take = take; } @Override public void run(){ if(take){ while(true){ Object o = blocker.take(); if(o != null){ System.out.println(o.toString()); } } }else{ Object o = new Object(); System.out.println("put,,," + o.toString()); blocker.put(o); } } } static class BlockerObject{ Queue<Object> inner = new LinkedList<Object>(); Queue<Thread> twaiters = new LinkedList<Thread>(); Queue<Thread> pwaiters = new LinkedList<Thread>(); public void put(Object o){ inner.offer(o); pwaiters.offer(Thread.currentThread()); Thread t = twaiters.poll(); if(t != null){ LockSupport.unpark(t); } System.out.println("park"); LockSupport.park(Thread.currentThread()); System.out.println("park is over"); } public Object take(){ Thread t = pwaiters.poll(); if(t != null){ System.out.println("unpark"); LockSupport.unpark(t); System.out.println("unpark is OK"); } //twaiters.offer(Thread.currentThread()); return inner.poll(); } } } 备注:有时候会疑惑wait()/notify() 和Unsafe.park()/unpark()有什么区别?区别是wait和notify是Object类的方法,它们首选需要获得“对象锁”,并在synchronized同步快中执行。park和unpark怎不需要这么做。wait和park都是有当前线程发起,notify和unpark都是其他线程发起。wait针对的是对象锁,park针对的线程本身,但是最终的效果都是导致当前线程阻塞。Unsafe不建议开发者直接使用。

景凌凯 2020-04-24 16:41:16 0 浏览量 回答数 0

试用中心

为您提供0门槛上云实践机会,企业用户最高免费12个月
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 云栖号物联网 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 云栖号弹性计算 阿里云云栖号 云栖号案例 云栖号直播