论述题 2.  多线程同步有几种实现方法?
【正确答案】当使用多线程访问同一个资源时,非常容易出现线程安全的问题(例如,当多个线程同时对一个数据进行修改时,会导致某些线程对数据的修改丢失)。因此,需要采用同步机制来解决这种问题。Java主要提供了三种实现同步机制的方法:
   (1)synchronized关键字
   在Java语言中,每个对象都有一个对象锁与之相关联,该锁表明对象在任何时候只允许被一个线程所拥有,当一个线程调用对象的一段synchronized代码时,首先需要获取这个锁,然后去执行相应的代码,执行结束后,释放锁。
   synchronized关键字主要有两种用法(synchronized方法和synchronized块),此外该关键字还可以作用于静态方法、类或某个实例,但这都对程序的效率有很大的影响。
   1)synchronized方法。在方法的声明前加入synchronized关键字。例如:
   public synchronized void mutiThreadAccess();
   只要把多个线程访问的资源的操作放到mutiThreadAccess方法中,就能够保证这个方法在同一时刻只能被一个线程访问,从而保证了多线程访问的安全性。然而,当一个方法的方法体规模非常大时,把该方法声明为synchronized会大大影响程序的执行效率。为了提高程序的执行效率,Java语言提供了synchronized块。
   2)synchronized块。可以把任意的代码段声明为synchronized,也可以指定上锁的对象,有非常高的灵活性。用法如下:
   synchronized (syncObject) {
   //访问syncObject的代码
   }
   (2)wait与notify
   当使用synchronized来修饰某个共享资源的时候,如果线程A1在执行synchronized代码,另外一个线程A2也要同时执行同一对象的同一synchronized代码时,线程A2将要等到线程A1执行完成后,才能继续执行。在这种情况下,可以使用wait方法和notify方法。
   在synchronized代码被执行期间,线程可以调用对象的wait方法,释放对象锁,进入等待状态,并且可以调用notify方法或notifyAll方法通知正在等待的其他线程,notify方法仅唤醒一个线程(等待队列中的第一个线程),并允许它去获得锁,而notifyAll方法唤醒所有等待这个对象的线程,并允许它们去获得锁(并不是让所有唤醒线程都获取到锁,而是让它们去竞争)。
   (3)Lock
   JDK5新增加了Lock接口以及它的一个实现类ReentrantLock(重入锁),Lock也可以用来实现多线程的同步,具体而言,它提供了如下的一些方法来实现多线程的同步:
   1)lock()。以阻塞的方式来获取锁,也就是说,如果获取到了锁,则立即返回,如果其他线程持有锁,当前线程等待,直到获取锁后返回。
   2)tryLock()。以非阻塞的方式获取锁。只是尝试性地去获取一下锁,如果获取到锁,则立即返回true,否则,立即返回false。
   3)tryLock(long timeout,TimeUnit unit)。如果获取了锁,立即返回true,否则,会等待参数给定的时间单元,在等待的过程中,如果获取了锁,就返回true,如果等待超时,则返回false。
   4)lockInterruptibly()。如果获取了锁,则立即返回,如果没有获取锁,则当前线程处于休眠状态,直到获得锁,或者当前线程被其他线程中断(会收到InterruptedException异常)。它与lock()方法最大的区别在于:如果lock()方法获取不到锁,则会一直处于阻塞状态,且会忽略interrupt()方法。如下例所示:
   import java.util.concurrent.locks.Lock;
   import java.util.concurrent.locks.ReentrantLock;
   public classTest{
   public static void main(String[]args) throws InterruptedException{
   final Lock lock=new ReentrantLock();
   lock.lock();
   Thread t1=new Thread(new Runnable(){
   public void run(){
   try{
   lock.lockInterruptibly();
   //lock.lock();编译器报错
   }catch(InterruptedException e){
   System.out.println("interrupted.");
   }
   }
   });
   t1.start();
   t1.interrupt();
   Thread.sleep(1);
   }
   }
   程序的运行结果为:
   interrupted.
   如果把lock.lockInterruptibly()替换为lock.lock(),编译器将会提示lock.lock()catch代码块无效,因为lock.lock()不会抛出异常,由此可见,lock()方法会忽略interrupt()引发的异常。
【答案解析】