当前位置: 首页 > 后端技术 > Java

ReenTrantLock和Synchronized

时间:2023-04-01 20:41:04 Java

java中常用的排他锁有两种:Synchronized和ReenTrantLock。两种锁在性能上差别不大,但ReenTrantLock是手动加锁和解锁,更灵活,功能更多独占锁,可重入锁publicstaticvoidmain(String[]args){//可重入锁:一个线程多次获取同一个对象的锁。可重入锁的意义之一就是防止死锁。实现原理:当一个线程获得锁时,jvm会记录锁的所有者,并将锁中的counter加1,当同一个线程再次获得锁时,counter会再次加1。对于未被占用的锁,计数器为0。当线程退出synchronized块时,计数器值会递减,直到计数器值为0,锁被释放。只有其他线程才能获取独占锁:一把锁一次只能被一个线程占用method();方法1();锁定。解锁();锁定。解锁();}publicstaticvoidmethod(){lock.lock();System.out.println("第一次获取锁");}publicstaticvoidmethod1(){lock.lock();System.out.println("第二次获取锁");}}这里在method1中获取了TestReenTrantLock类锁,在调用method1时再次获取了TestReenTrantLock类锁。这是一个可以实现公平锁的可重入锁。publicclassFairReenTrantLock{/***公平锁:当锁可用时,在锁上的等待时间最短长线程获得锁的使用权*无参数或fasle,为非公平锁*非公平锁:随机获取锁的使用权*/staticLocklock=newReentrantLock(true);publicstaticvoidmain(String[]args){for(inti=0;i<5;i++){newThread(newThreadDemo()).start();}}staticclassThreadDemoimplementsRunnable{publicThreadDemo(){}@Overridepublicvoidrun(){try{TimeUnit.秒。睡觉(2);}赶上(异常e){e。打印堆栈跟踪();}for(inti=0;i<2;i++){lock.lock();System.out.println("获得锁的线程"+Thread.currentThread().getName());锁定。解锁();}}}}可以响应线程中断先了解publicvoidinterrupt();(属于ThreadL类)publicclassTestInterrupt{publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadtestThread=newTestThread();测试线程.start();测试线程.中断();}/**下面测试的三个方法都会阻塞线程。如果三个方法都不使用,线程会直接结束,*并且isInterrupted()为true*使用其中一个,会抛出InterruptedException,isInterrupted()为false*/staticclassTestThreadextendsThread{@Overridepublicvoidrun(){System.out.println("线程开始运行");尝试{//TimeUnit.SECONDS.sleep(5);//等待();加入();}catch(Exceptione){System.out.println("线程异常"+e);小号system.out.println(Thread.currentThread().isInterrupted());}System.out.println("线程运行完毕");}}}中断情况的响应publicclassLockInterruptibly{staticLockfirstLock=newReentrantLock();静态锁secondLock=newReentrantLock();publicstaticvoidmain(String[]args){Threadfirstthread=newThread(newThreadDemo(firstLock,secondLock));线程secondThread=newThread(newThreadDemo(secondLock,firstLock));firstthread.start();secondThread.start();firstthread.interrupt();}staticclassThreadDemoimplementsRunnable{privateLockfirstLock;私有锁secondLock;publicThreadDemo(LockfirstLock,LocksecondLock){this.firstLock=firstLock;这个.secondLock=secondLock;}//它会中断等待线程,释放锁,然后另一个线程获取锁,继续走@Overridepublicvoidrun(){try{firstLock.lockInte破坏性地();TimeUnit.SECONDS.sleep(1);secondLock.lockInterruptibly();}catch(Exceptione){e.printStackTrace();}最后{firstLock.unlock();secondLock.unlock();System.out.println(Thread.currentThread().getName()+"正常绑定");}}}}不响应中断例publicclassTestSynchronized{staticObjectresource1=newObject();静态对象资源2=新对象();publicstaticvoidmain(String[]args){线程thread1=newTestSynchronizedThread(resource1,resource2);线程thread2=newTestSynchronizedThread(resource2,resource1);thread1.start();thread2.start();thread1.interrupt();//中断线程序,}staticclassTestSynchronizedThreadextendsThread{Objectresource1;对象资源2;公共TestSynchronizedThread(对象资源1,对象资源2){this.resource1=resource1;this.resource2=resource2;}//即使发生异常,线程也不会被中断,两个线程继续等待对方的资源,造成死锁@Overridepublicvoidrun(){synchronized(resource1){System.out.println(Thread.currentThread().getName()+"--111");尝试{TimeUnit.SECONDS.sleep(1);System.out.println(Thread.currentThread().getName()+"--222");}catch(InterruptedExceptione){System.out.println(Thread.currentThread().getName()+"--333");}System.out.println(Thread.currentThread().getName()+"--444");同步(resource2){System.out.println(555);}}}}}获取锁时,等待限时publicclassTestTryLock{staticLocklock=newReentrantLock();publicstaticvoidmain(String[]args){Threadthread=newThread(){@Overridepublicvoidrun(){if(lock.tryLock()){//立即返回获取锁的结果,true表示获取锁成功,false表示失败System.out.println("线程获取锁");}try{if(lock.tryLock(5,TimeUnit.SECONDS)){System.out.println("为了获取锁,最多等待5s");}}catch(InterruptedExceptione){e.printStackTrace();}}};thread.start();}}使用condition实现等待通知机制,publicclassTestCondition{staticLocklock=newReentrantLock();静态条件条件=lock.newCondition();publicstaticvoidmain(String[]args)throwsInterruptedException{lock.lock();System.out.println("主线程获取锁");新线程(新的TestConditionThread())。开始();condition.await();//主线程等待,释放锁System.out.println("main线程被唤醒");lock.unlock();}//使用Condition前必须先获取锁,调用condition的await()使当前线程释放锁,处于一个等待状态//直到其他线程调用相同条件的信号()被唤醒staticclassTestConditionThreadextendsThread{@Overridepublicvoidrun(){System.out.println("启动子线程"+Thread.currentThread().getName());lock.lock();//子线程获取锁System.out.println("子线程获取锁");condition.signal();//子线程释放锁并唤醒主线程lock.unlock();}}}condition实现阻塞队列=lock.newCondition();privateLinkedListlist=newLinkedList<>();publicTestBlockingQueue(intsize){this.size=size;}/***使用ReentrantLock实现阻塞队列。当队列满了,入队阻塞*@paramdata*/publicvoidenqueue(Integerdata){try{锁.锁();while(list.size()==size){full.await();//队列已满,阻塞当前线程,防止入队}list.add(data);System.out.println("入队成功"+data);空信号();}catch(Exceptione){e.printStackTrace();}最后{lock.unlock();}}publicvoiddequeue(){try{锁。锁();while(list.size()==0){empty.await();//队列为空,阻塞出队}Integere=list.removeFirst();System.out.println("出队成功"+e);全信号();}catch(异常e){}finally{lock.unlock();}}publicstaticvoidmain(String[]args){TestBlockingQueuetestBlockingQueue=newTestBlockingQueue(3);for(inti=0;i<9;i++){intdata=i;新线程(新的Runnable(){@Overridepublicvoidrun(){testBlockingQueue.enqueue(数据);}})。开始();}for(inti=0;i<5;i++){newThread(newRunnable(){@Overridepublicvoidrun(){testBlockingQueue.dequeue();}}).start();}}}synchronized作用:解决多线程间访问资源时的线程安全问题,保证其修改的代码同时只有一个线程访问重量级锁:监视器锁(monitor)依赖于底层操作系统的互斥锁来实现,java线程映射到操作系统的native线程上。因此,挂起或唤醒线程需要操作系统的帮助。操作系统在线程切换时需要从用户态切换到内核态。这些状态之间的转换需要很长时间,既费时又费钱。不过java6之后,对synchronized做了很多优化。Synchronized的用法1)修改common方法,锁定当前实例对象。如果要进入方法,需要先获取当前实例对象的锁。Synchronized(this)类对象被锁定。如果要进入方法,需要先获取类对象的锁。同步(类)。实例对象锁和类锁不互斥,互不影响。3)修改代码块,锁定给定对象,可以是实例对象,也可以是类对象Synchronized(this|object)4)尽量不要使用Synchronized(Stirng),因为jvm中的字符串常量池具有缓存功能5)如果类中存在共享成员变量,即使方法同步,也会存在线程安全问题,因为被Synchronized锁定的方法和代码片段不会锁定属性publicclassSynchronizedTest{Listlist=Collections.synchronizedList(newArrayList<>());//A、B线程执行该方法publicsynchronizedBooleanputIfAbsent(Integeri){//A线程完成这一步,B线程开始执行,B执行完成后,A再次执行,元素会重复添加booleanflag=!list.contains(i);如果(标志){list.add(i);}返回标志;}}