网站程序开发公司北京公司网站设计价格
web/
2025/10/5 11:29:58/
文章来源:
网站程序开发公司,北京公司网站设计价格,设计师要考什么证,做网站策划书在网上看来很多关于同步锁的博文#xff0c;记录下来方便以后阅读 一、Lock和synchronized有以下几点不同#xff1a; 1#xff09;Lock是一个接口#xff0c;而synchronized是Java中的关键字#xff0c;synchronized是内置的语言实现#xff0c;synchronized是在JVM层面…在网上看来很多关于同步锁的博文记录下来方便以后阅读 一、Lock和synchronized有以下几点不同 1Lock是一个接口而synchronized是Java中的关键字synchronized是内置的语言实现synchronized是在JVM层面上实现的不但可以通过一些监控工具监控synchronized的锁定而且在代码执行时出现异常JVM会自动释放锁定但是使用Lock则不行lock是通过代码实现的要保证锁定一定会被释放就必须将 unLock()放到finally{} 中 2synchronized在发生异常时会自动释放线程占有的锁因此不会导致死锁现象发生而Lock在发生异常时如果没有主动通过unLock()去释放锁则很可能造成死锁现象因此使用Lock时需要在finally块中释放锁 3Lock可以让等待锁的线程响应中断线程可以中断去干别的事务而synchronized却不行使用synchronized时等待的线程会一直等待下去不能够响应中断 4通过Lock可以知道有没有成功获取锁而synchronized却无法办到。 5Lock可以提高多个线程进行读操作的效率。 在性能上来说如果竞争资源不激烈两者的性能是差不多的而当竞争资源非常激烈时即有大量线程同时竞争此时Lock的性能要远远优于synchronized。所以说在具体使用时要根据适当情况选择。 举个例子当有多个线程读写文件时读操作和写操作会发生冲突现象写操作和写操作会发生冲突现象但是读操作和读操作不会发生冲突现象。 但是采用synchronized关键字来实现同步的话就会导致一个问题 如果多个线程都只是进行读操作所以当一个线程在进行读操作时其他线程只能等待无法进行读操作。 因此就需要一种机制来使得多个线程都只是进行读操作时线程之间不会发生冲突通过Lock就可以办到。 另外通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的 二、ReentrantLock获取锁定与三种方式 a) lock(), 如果获取了锁立即返回如果别的线程持有锁当前线程则一直处于休眠状态直到获取锁 b) tryLock(), 如果获取了锁立即返回true如果别的线程正持有锁立即返回false c)tryLock(long timeout,TimeUnit unit) 如果获取了锁定立即返回true如果别的线程正持有锁会等待参数给定的时间在等待的过程中如果获取了锁定就返回true如果等待超时返回false d) lockInterruptibly:如果获取了锁定立即返回如果没有获取锁定当前线程处于休眠状态直到或者锁定或者当前线程被别的线程中断 三、下面我们就来探讨一下java.util.concurrent.locks包中常用的类和接口。 1.Lock 首先要说明的就是Lock通过查看Lock的源码可知Lock是一个接口 1 2 3 4 5 6 7 8 public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); } 下面来逐个讲述Lock接口中每个方法的使用lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。newCondition()这个方法暂且不在此讲述会在后面的线程协作一文中讲述。 在Lock中声明了四个方法来获取锁那么这四个方法有何区别呢 首先lock()方法是平常使用得最多的一个方法就是用来获取锁。如果锁已被其他线程获取则进行等待。 由于在前面讲到如果采用Lock必须主动去释放锁并且在发生异常时不会自动释放锁。因此一般来说使用Lock必须在try{}catch{}块中进行并且将释放锁的操作放在finally块中进行以保证锁一定被被释放防止死锁的发生。通常使用Lock来进行同步的话是以下面这种形式去使用的 1 2 3 4 5 6 7 8 9 Lock lock ...; lock.lock(); try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 } tryLock()方法是有返回值的它表示用来尝试获取锁如果获取成功则返回true如果获取失败即锁已被其他线程获取则返回false也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。 tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的只不过区别在于这个方法在拿不到锁时会等待一定的时间在时间期限之内如果还拿不到锁就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁则返回true。 所以一般情况下通过tryLock来获取锁时是这样使用的 1 2 3 4 5 6 7 8 9 10 11 12 Lock lock ...; if(lock.tryLock()) { try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 } }else { //如果不能获取锁则直接做其他事情 } lockInterruptibly()方法比较特殊当通过这个方法去获取锁时如果线程正在等待获取锁则这个线程能够响应中断即中断线程的等待状态。也就使说当两个线程同时通过lock.lockInterruptibly()想获取某个锁时假若此时线程A获取到了锁而线程B只有在等待那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。 由于lockInterruptibly()的声明中抛出了异常所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。 因此lockInterruptibly()一般的使用形式如下 1 2 3 4 5 6 7 8 9 public void method() throws InterruptedException { lock.lockInterruptibly(); try { //..... } finally { lock.unlock(); } } 注意当一个线程获取了锁之后是不会被interrupt()方法中断的。因为本身在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程中的线程只能中断阻塞过程中的线程。 因此当通过lockInterruptibly()方法获取某个锁时如果不能获取到只有进行等待的情况下是可以响应中断的。 而用synchronized修饰的话当一个线程处于等待某个锁的状态是无法被中断的只有一直等待下去。 2.ReentrantLock ReentrantLock意思是“可重入锁”关于可重入锁的概念在下一节讲述。ReentrantLock是唯一实现了Lock接口的类并且ReentrantLock提供了更多的方法。下面通过一些实例看具体看一下如何使用ReentrantLock。 例子1lock()的正确使用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Test { private ArrayListInteger arrayList new ArrayListInteger(); public static void main(String[] args) { final Test test new Test(); new Thread(){ public void run() { test.insert(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.insert(Thread.currentThread()); }; }.start(); } public void insert(Thread thread) { Lock lock new ReentrantLock(); //注意这个地方 lock.lock(); try { System.out.println(thread.getName()得到了锁); for(int i0;i5;i) { arrayList.add(i); } } catch (Exception e) { // TODO: handle exception }finally { System.out.println(thread.getName()释放了锁); lock.unlock(); } } } 各位朋友先想一下这段代码的输出结果是什么 View Code 也许有朋友会问怎么会输出这个结果第二个线程怎么会在第一个线程释放锁之前得到了锁原因在于在insert方法中的lock变量是局部变量每个线程执行该方法时都会保存一个副本那么理所当然每个线程执行到lock.lock()处获取的是不同的锁所以就不会发生冲突。 知道了原因改起来就比较容易了只需要将lock声明为类的属性即可。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Test { private ArrayListInteger arrayList new ArrayListInteger(); private Lock lock new ReentrantLock(); //注意这个地方 public static void main(String[] args) { final Test test new Test(); new Thread(){ public void run() { test.insert(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.insert(Thread.currentThread()); }; }.start(); } public void insert(Thread thread) { lock.lock(); try { System.out.println(thread.getName()得到了锁); for(int i0;i5;i) { arrayList.add(i); } } catch (Exception e) { // TODO: handle exception }finally { System.out.println(thread.getName()释放了锁); lock.unlock(); } } } 这样就是正确地使用Lock的方法了。 例子2tryLock()的使用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Test { private ArrayListInteger arrayList new ArrayListInteger(); private Lock lock new ReentrantLock(); //注意这个地方 public static void main(String[] args) { final Test test new Test(); new Thread(){ public void run() { test.insert(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.insert(Thread.currentThread()); }; }.start(); } public void insert(Thread thread) { if(lock.tryLock()) { try { System.out.println(thread.getName()得到了锁); for(int i0;i5;i) { arrayList.add(i); } } catch (Exception e) { // TODO: handle exception }finally { System.out.println(thread.getName()释放了锁); lock.unlock(); } } else { System.out.println(thread.getName()获取锁失败); } } } 输出结果 View Code 例子3lockInterruptibly()响应中断的使用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class Test { private Lock lock new ReentrantLock(); public static void main(String[] args) { Test test new Test(); MyThread thread1 new MyThread(test); MyThread thread2 new MyThread(test); thread1.start(); thread2.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } thread2.interrupt(); } public void insert(Thread thread) throws InterruptedException{ lock.lockInterruptibly(); //注意如果需要正确中断等待锁的线程必须将获取锁放在外面然后将InterruptedException抛出 try { System.out.println(thread.getName()得到了锁); long startTime System.currentTimeMillis(); for( ; ;) { if(System.currentTimeMillis() - startTime Integer.MAX_VALUE) break; //插入数据 } } finally { System.out.println(Thread.currentThread().getName()执行finally); lock.unlock(); System.out.println(thread.getName()释放了锁); } } } class MyThread extends Thread { private Test test null; public MyThread(Test test) { this.test test; } Override public void run() { try { test.insert(Thread.currentThread()); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()被中断); } } } 运行之后发现thread2能够被正确中断。 3.ReadWriteLock ReadWriteLock也是一个接口在它里面只定义了两个方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public interface ReadWriteLock { /** * Returns the lock used for reading. * * return the lock used for reading. */ Lock readLock(); /** * Returns the lock used for writing. * * return the lock used for writing. */ Lock writeLock(); } 一个用来获取读锁一个用来获取写锁。也就是说将文件的读写操作分开分成2个锁来分配给线程从而使得多个线程可以同时进行读操作。下面的ReentrantReadWriteLock实现了ReadWriteLock接口。 4.ReentrantReadWriteLock ReentrantReadWriteLock里面提供了很多丰富的方法不过最主要的有两个方法readLock()和writeLock()用来获取读锁和写锁。 下面通过几个例子来看一下ReentrantReadWriteLock具体用法。 假如有多个线程要同时进行读操作的话先看一下synchronized达到的效果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Test { private ReentrantReadWriteLock rwl new ReentrantReadWriteLock(); public static void main(String[] args) { final Test test new Test(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); } public synchronized void get(Thread thread) { long start System.currentTimeMillis(); while(System.currentTimeMillis() - start 1) { System.out.println(thread.getName()正在进行读操作); } System.out.println(thread.getName()读操作完毕); } } 这段程序的输出结果会是直到thread1执行完读操作之后才会打印thread2执行读操作的信息。 View Code 而改成用读写锁的话 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Test { private ReentrantReadWriteLock rwl new ReentrantReadWriteLock(); public static void main(String[] args) { final Test test new Test(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); new Thread(){ public void run() { test.get(Thread.currentThread()); }; }.start(); } public void get(Thread thread) { rwl.readLock().lock(); try { long start System.currentTimeMillis(); while(System.currentTimeMillis() - start 1) { System.out.println(thread.getName()正在进行读操作); } System.out.println(thread.getName()读操作完毕); } finally { rwl.readLock().unlock(); } } } 此时打印的结果为 View Code 说明thread1和thread2在同时进行读操作。 这样就大大提升了读操作的效率。 不过要注意的是如果有一个线程已经占用了读锁则此时其他线程如果要申请写锁则申请写锁的线程会一直等待释放读锁。 如果有一个线程已经占用了写锁则此时其他线程如果申请写锁或者读锁则申请的线程会一直等待释放写锁。 关于ReentrantReadWriteLock类中的其他方法感兴趣的朋友可以自行查阅API文档。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/87338.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!