多线程同步有几种实现方法?

           答案:当使用多线程访问同一个资源时,非常容易出现线程安全的问题(例如,当多线程同时对一个数据进行修改时,会导致某些线程对数据的修改丢失)。因此,需要采用

同步机制来解决这种问题。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 class LockTest {

    public static void main(String[] args) throws InterruptedException{
        final Lock lock = new ReentrantLock();
        lock.lock();
        Thread t1 = new Thread(new Runnable() {
            @Override
            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()引发的异常。

posted @ 2022-01-26 21:51  杜嘟嘟  阅读(451)  评论(0编辑  收藏  举报