1、悲观锁和乐观锁
悲观锁:是不支持并发的,只能一个操作完后另一个再操作,频繁的上锁解锁,效率低但数据安全
乐观锁:支持并发,可以多个同时操作,但是引入了版本号概念,如果拿到的版本号和数据库的版本号不一致则操作失败,如A和B线程同时去修改余额,A先提交,那么版本号由v1.0改为v1.1后,B再次提交则匹配不上所以提交失败,下图可分别展示两个锁的区别
2、表锁和行锁
表锁:就是将整张表进行上锁,其余线程不能进行操作,其效率较低,但不会存在死锁问题
行锁:比如表里有四条数据,锁第一条数据叫行锁,行锁效率快但会存在存在互相等待死锁
3、读锁和写锁
读锁:也叫共享锁,可以多个线程同时访问一个数据,会发生死锁问题,比如A线程修改时候需要等B读完之后,B线程修改时候需要等A读完之后,可能造成互相等待的死锁问题
写锁:也叫独占锁,同一个数据只能一个线程写操作,也会存在死锁问题,比如A写完第一个条数据后去写第二个数据,B线程写完第二个数据后去写第一个数据,互相都没有释放其锁,造成互相等待死锁问题
总结:一个资源可以被多个线程访问(读锁共享锁),或者被一个线程进行写的操作(写锁独占锁),但是不能同时存在读写线程,读写互斥,读读共享
4、读写锁案例
使用ReadWriteLock的实现类ReentrantReadWriteLock,可以实现线程安全的读写操作,原理还是上锁机制,先写入再读取机制
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class lockDemo01 {
public static void main(String[] args) {
mapLock mapLock=new mapLock();
for (int i = 1; i <=5 ; i++) {
final int number = i;
new Thread(()->{
try {
mapLock.put(number+"",number+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
for (int i = 1; i <=5 ; i++) {
final int number = i;
new Thread(()->{
try {
mapLock.get(number+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
class mapLock{
private volatile Map<String,Object> map=new HashMap<>();
ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
public void put(String key,Object obj) throws InterruptedException {
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName()+" 即将进行写入");
TimeUnit.MICROSECONDS.sleep(300);
map.put(key,obj);
System.out.println(Thread.currentThread().getName()+"写入完成:"+key);
readWriteLock.writeLock().unlock();
}
public Object get(String key) throws InterruptedException {
readWriteLock.readLock().lock();
System.out.println(Thread.currentThread().getName()+" 即将进行取出");
TimeUnit.MICROSECONDS.sleep(300);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"取出完成:"+key);
readWriteLock.readLock().unlock();
return o;
}
}
5、读写锁优缺点
优点:读读共享,可以多个资源同时读取
缺点: a、锁饥饿问题,比如有100个读的操作,一个写的操作,那么可能都忽略了写的操作。b、读时候不能写,只有读完之后才能写,写操作可以读
6、锁降级
将写入锁降级为读锁,其流程如下图所示,其读锁不可升级为写锁
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class redwirtDemo01 {
public static void main(String[] args) {
ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock red = readWriteLock.readLock();
ReentrantReadWriteLock.WriteLock write = readWriteLock.writeLock();
//写锁加锁
write.lock();
System.out.println("准备开始操作了");
//获取读锁
red.lock();
System.out.println("锁降级成功");
//释放写锁 降级处理
write.unlock();
//释放读锁
red.unlock();
}
}
以上代码充分说明了锁降级过程,写锁可以降级为读锁,但是读锁不能升级为写锁,以下代码实验验证,经一下验证,读锁后获取不到写锁,升级成功打印不出,jvm未能结束
7、阻塞队列BlockingQueue