
在开发Java多线程应用程序中,各个线程之间由于要共享资源,必须用到锁机制。Java提供了多种多线程锁机制的实现方式,常见的有synchronized、ReentrantLock、Semaphore、AtomicInteger等。每种机制都有优缺点与各自的适用场景,必须熟练掌握他们的特点才能在Java多线程应用开发时得心应手。——《Java锁机制详解》。
线程同步有关的类图关系可用以下的图总结:

1、Java Concurrency API 中的 Lock 接口是什么?对比同步它有什么优势?
(3)线程2的run()方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁,线程2肯定是要等待线程1释放lock1的对象锁的。现在有两个线程竞争该资源的内置锁,在等待一段时间后,想要终止线程t2的锁获取等待操作,使用t2.interrupt()。(3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的。
关于API及代码的例子请移步:《java并发编程Lock》。常用接口方法如下:
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,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。通常使用Lock来进行同步的话,是以下面这种形式去使用的:
Lock lock = ...; lock.lock(); try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 }
// 释放对象上的锁并阻止当前线程,直到它重新获取该锁 // 如果指定的超时间隔已过,则线程进入就绪队列 // 该方法只有程重新获得锁时才有返回值 // 在超时等待时间内就获得了锁,返回结果为 true,否则为 false // 有时可以利用这个返回结果进行一个代码的分支处理 monitor.wait(monitor, 1000)。答:先生成一个对象obj,在一个全局map里put(id,obj)存放起来,再用synchronized获取obj锁,再调用obj.wait()让当前线程处于等待状态,然后另一消息线程等到服务端结果来了后,再map.get(id)找到obj,再用synchronized获取obj锁,再调用obj.notifyall()唤醒前面处于等待状态的线程。这里即保证了其他线程b对获取到初始化锁的线程a的重排序不可见,即线程a进入类的初始化并将status设置为正在初始化,当线程b进入初始化队列时,会获取到a设置的status并乖乖等待,直到a初始化结束后将status设置为初始化结束释放锁并唤醒其他等待线程,到这里a的初始化过程完成,b才被唤醒并获取锁接着读取status为初始化结束,b直接释放锁,到这里线程b的初始化过程完成。
Lock lock = ...; if(lock.tryLock()) { try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 } }else { //如果不能获取锁,则直接做其他事情 }
当a线程调用anyobject对象加入synchronized关健字的x方法时,a线程就获得了x方法所在对象的锁,所以其他线程必须等a线程执行完毕才可以调用x方法,而b线程如果调用声明了synchronized关键字的非x方法时,必须等a线程将x方法执行完,也就是释放对象锁后才可以调用。wait是object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyall)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 wait是object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyall)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
public void method() throws InterruptedException { lock.lockInterruptibly(); try { //..... } finally { lock.unlock(); }
a、在 java 中类只允许单一继承 b、在 java 中一个类只能实现一个接口 c、在 java 中一个类不能同时继承一个类和实现一个接口 d、在 java 中接口只允许单一继承 paint()方法中的参数可使用哪种类型 ( ) a、graphics b、graphics2d c、string d、color 以下不属于 java 基本数据类型的是( ) a、int b、boolean c、float d、char 以下哪项可能包含菜单条( )。举个例子:一个类a需要用到接口b中的方法,那么就需要为类a和接口b建立关联或依赖关系,最原始的方法是在类a中创建一个接口b的实现类c的实例,但这种方法需要开发人员自行维护二者的依赖关系,也就是说当依赖关系发生变动的时候需要修改代码并重新构建整个系统。当使用new创建b的实例时,因为b继承了a类,a的构造函数首先会被运行,但应当注意的是,虽然运行的是a的构造函数,但在a的构造函数中调用的函数首先是在b类中查找,因为printfields已被重写,所以最终运行b类中的printfields函数。
public class Test { private ArrayList<Integer> arrayList = new ArrayList<Integer>(); 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(); //可以用Java箭头函数特性改写上述冗余代码: // new Thread(){()->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 i=0;i<5;i++) { arrayList.add(i); } } catch (Exception e) { // TODO: handle exception }finally { System.out.println(thread.getName()+"释放了锁"); lock.unlock(); } }
完成读/写之后将mutex设置成开锁状态,并唤醒阻塞在该互斥锁上的线程。顺序锁也是对读写锁的一种优化,对于顺序锁,读者绝不会被写者阻塞,也就说,读者可以在写者对被顺序锁保护的共享资源进行写操作时仍然可以继续读,而不必等待写者完成写操作,写者也不需要等待所有读者完成读操作才去进行写操作。readwritelock是一个读写锁接口,reentrantreadwritelock是readwritelock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
2、ReadWriteLock是什么?
读和写是两种不同的功能,读-读不互斥,读-写互斥,写-写互斥。现在假如我们有2个进程都同时读,如果我们使用上面的互斥锁和条件变量,当其中一个进程在读取数据的时候,另一个进程只能等待,因为它得不到锁。现在假如我们有2个进程都同时读,如果我们使用上面的互斥锁和条件变量,当其中一个进程在 读取数据的时候,另一个进程只能等待,因为它得不到锁。
读线程从共享资源中读数据,同时写线程需要向共享资源中写数据,当多个读、写线程共用一个资源(缓冲区)的时候,据需要使用诸如critical section或者互斥量的排他性锁(独占锁)来控制资源的访问,但是针对读操作较为频繁,写操作相对较少的情况下,使用独占锁是很不划算的,需要耗费更多的时间和其他资源。读线程从共享资源中读数据,同时写线程需要向共享资源中写数据,当多个读、写线程共用一个资源(缓冲区)的时候,据需要使用诸如criticalsection或者互斥量的排他性锁(独占锁)来控制资源的访问,但是针对读操作较为频繁,写操作相对较少的情况下,使用独占锁是很不划算的,需要耗费更多的时间和其他资源。确保它们互不冲突,也就是必须阻止b线程在a线程读数据的过程中向链表里面写数据(a获得了锁,b必须等a释放了该锁)。
3、锁机制有什么用
数据并发访问所产生的问题,在有些场景下可能是允许的,但是有些场景下可能就是致命的,通常会通过锁机制来解决数据并发访问问题,按锁定对象不同可以分为表级锁和行级锁。在linux中,常用的线程同步方法有互斥量( mutex )、读写锁和条件变量,合理使用这三种方法可以保证数据的一致性,但值得的注意的是,在设计应用程序时,所有的线程都必须遵守相同的数据访问规则为前提,才能保证这些同步方法有效,如果允许某个线程在没有得到访问权限(比如锁)的情况下访问共享资源,那么其他线程在使用共享资源前都获得了锁,也会出现数据不一致的问题。不是用于交换大批数据,而用于多线程之间的同步.常作为一种锁机制,防止某进程在访问资源时其它进程也访问该资源。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-107573-1.html
粉丝的热情褪去了