在我们的操作系统上运行着很多的计算机程序,每一个计算机程序可能都包含一个或多个独立的线程,而这些线程若访问我们系统上的共享资源时,比如说:内存资源、文件资源、数据库资源等。可能会使得各线程之间访问资源时产生冲突。那我们如何来解决这种问题呢?Java中存在这么几种处理方式:同步代码块、同步方法、同步锁等。
一、同步代码块
同步代码块格式:synchronized(key){
//存放同步代码块;
}
如何来理解同步代码块呢?打个比方:一个object就像一个大房子,大门永远打开。房子里有很多房间(也就是方法)。这些房间有上锁的(synchronized方法),和不上锁之分(普通方法)。房门口放着一把钥匙(key),这把钥匙可以打开所有上锁的房间。另外我把所有想调用该对象方法的线程比喻成想进入这房子某个房间的人。所有的东西就这么多了,下面我们看看这些东西之间如何作用的。
一个人想进入某间上了锁的房间,他来到房子门口,看见钥匙在那儿(说明暂时还没有其他人 要使用上锁的房间)。于是他走上去拿到了钥匙,并且按照自己的计划使用那些房间。注意一点,他每次使用完一次上锁的房间后会马上把钥匙还回去。即使他要连 续使用两间上锁的房间,中间他也要把钥匙还回去,再取回来。
因此,普通情况下钥匙的使用原则是:“随用随借,用完即还。”
这时其他人可以不受限制的使用那些不上锁的房间,一个人用一间可以,两个人用一间也可以,没限制。但是如果当某个人想要进入上锁的房间,他就要跑到大门口去看看了。有钥匙当然拿了就走,没有的话,就只能等了。
当多线程抢占方法体内的资源时,只有获取key这个对象的线程才可以获得并执行方法体中的内容,其它的线程必须等待这个线程执行完毕后归还key对象,才可以申请获得这个key来抢占资源。
二、同步方法
同步方法的语法格式:public synchronized 数据返回类型 方法名(){} 。就是使用 synchronized 这个关键字来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,这个同步监视器也就是同步代码块中的“key”;同步方法的同步监视器是 this 也就是当前对象,通过使用同步方法,可非常方便的将某类变成线程安全的类,具有如下特征:
1,该类的对象可以被多个线程安全的访问。
2,每个线程调用该对象的任意方法之后,都将得到正确的结果。
3,每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。
注:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等。
三、同步锁
在Jdk1.5之后,java提供了一个强大的线程同步机制:通过显式定义同步锁对象来实现同步,在这种机制下,同步锁由Lock对象充当。
锁,提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应该首先获Lock对象。
某些锁可能允许对共享资源并发访问,如ReadWriteLock(读写锁)。Jdk1.8之后新增了新型的StampedLock。它在大多数场景中可以替代传统的ReentranReadWriteLock。为读写操作提供三种锁模式:Writing、ReadingOptimistic、Reading。
当产生Lock对象时,我们可以直接通过Lock对象的lock()方法和unlock()方法就可以实现对资源的上锁和解锁过程。