Java多线程之锁

来源:互联网 发布:大数据平台优势 编辑:程序博客网 时间:2024/05/21 22:22

1、线程安全

  当多个线程访问一个对象的时候,如果不用考虑这些线程在运行时环境的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。

  反之,如果多个线程访问一个对象,线程之间的有先后顺序,或者线程之间的交互会影响最终的运行结果,那么这个对象就是非线程安全的,如存放一个数组,不同线程的访问可能导致数组最终的存放状态不一样。

2、线程不安全是怎样产生的

  Java内存模型规定了所有变量都存储在主内存中,每个线程还有自己的工作内存,线程的工作内存中保存了改线程使用到的变量的主内存副本拷贝,线程对于变量的所有操作(读取和赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量,不同线程之间无法直接访问对方工作内存中的变量,线程间值的传递均需要通过主内存来完成,所以会产生传递值不一致的情况。

3、如何实现线程安全

    1)互斥同步,最常见的并发正确性保障手段,同步至多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用。互斥同步的主要问题就是进行线程的阻塞和唤醒所带来的性能问题,因此这个同步也被称为阻塞同步。

  2)非阻塞同步,如果没有其他线程争用共享数据,那操作就成功了,如果共享数据有争用,产生了冲突,那就再进行其他的补偿措施(最常见的措施就是不断的重试,直到成功为止),这种策略不需要把线程挂起,所以这种同步也被称为非阻塞同步。

  3)无同步方案,简单的理解就是没有共享变量需要不同的线程去争用,目前有两种方案,一个是“可重入代码”,这种代码可以在执行的任何时刻中断它,转而去执行其他的另外一段代码,当控制权返回时,程序继续执行,不会出现任何错误。一个是“线程本地存储”,如果变量要被多线程访问,可以使用volatile关键字来声明它为“易变的“,以此来实现多线程之间的可见性。同时也可以通过ThreadLocal来实现线程本地存储的功能,一个线程的Thread对象中都有一个ThreadLocalMap对象,来实现KV数据的存储。

4、Java中的锁

   1)内置锁

   Java提供了一种内置的锁机制来支持原子性:synchronized关键字 。

   JVM中每个对象和类实际上都与一把锁与之相关联,对于对象来说,监视的是这个对象变量,对于类来说,监视的是类变量。当虚拟机装载类时,会创建一个Class类的实例,锁住的实际上是这个类对应的Class类的实例。对象锁是可重入的,也就是说一个对象或者类上的锁是可以累加的。一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁); 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized修饰的代码);线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻只有一个线程在执行。

   2)显示锁

 在JDK5之前,可使用的同步机制只有synchronized 关键字和volative变量,JDK5提供的锁工具类都在java.util.concurrent.locks包下,有Condition、Lock、ReadWriteLock等接口:Lock的实现包括DummyConcurrentLock, NonReentrantLock, ReadLock, WriteLock, ReentrantLock;ReadWriteLock的实现类有ReentrantReadWriteLock。

ReentrantLock有几个高级特性:等待可中断,当持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,改为处理其他事情;可以实现公平锁,公平锁指多个线程在等待同一个锁时,必须按照申请锁的顺序依次获得锁,synchronized是非公平锁,ReentrantLock默认也是非公平的,只不过可以通过构造函数来制定实现公平锁;

  3)几个相关的概念

 可重入锁 :可重入锁的概念是自己可以再次获取自己的内部锁;

  读写锁:读写锁拆成读锁和写锁来理解。读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能只有一个线程拥有,而且获取写锁的时候其他线程都已经释放了读锁,而且该线程获取写锁之后,其他线程不能再获取读锁。

  公平与非公平锁:公平表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO顺序。而非公平就是一种获取锁的抢占机制,和公平相对就是先来不一定先得,这个方式可能造成某些线程饥饿



0 0