1、公平锁和非公平锁
公平锁:是指多线程按照申请锁的顺序来获取锁
非公平锁:是指多个线获取锁的顺序不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获得锁。在高并发的情况下,有可能造成优先级反转或者饥饿的情况。
- 非公平锁如果占有锁失败,则采用公平锁方式
- ReentrantLock,Synchroized默认采用非公平锁
- 非公平锁的优点在于吞吐量比公平锁大
public class test04 {
public static void sync(String tips) {
synchronized (test04.class) {
System.out.println(tips);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(()->sync("线程1")).start();
// Thread.sleep(100);
new Thread(()->sync("线程2")).start();
// Thread.sleep(100);
new Thread(()->sync("线程3")).start();
// Thread.sleep(100);
new Thread(()->sync("线程4")).start();
}
}
输出
因为是非公平锁,所以后面的线程可以插队
2、可重入锁(递归锁)
指的是同一线程外层函数获得锁,内层递归函数仍能获得锁
同一线程外层方法获得锁,进入内层方法自动获得锁
线程可以进入任何一个它已经拥有的锁所同步着的代码块
- 最大作用是避免死锁
- ReentrantLock,Synchroized都是可重入锁
Synchroized锁
class Phone {
public synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getId()+"\t invoked sendSMS()");
sendEmail();
}
public synchronized void sendEmail() throws Exception{
System.out.println(Thread.currentThread().getId()+"\t #####invoked sendEmail()");
}
}
public class test04 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(new Runnable() {
@Override
public void run() {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}
},"t1").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}
},"t2").start();
}
}
lock锁
class Phone implements Runnable{
Lock lock = new ReentrantLock();
@Override
public void run() {
get();
public void get() {
lock.lock();
try{
System.out.println(Thread.currentThread().getId()+"\t invoked get()");
set();
}finally {
lock.unlock();
}
}
public void set() {
lock.lock();
try{
System.out.println(Thread.currentThread().getId()+"\t #####invoked set()");
set();
}finally {
lock.unlock();
}
}
}
public class test04 {
public static void main(String[] args) {
Thread t3= new Thread(phone);
Thread t4= new Thread(phone);
t3.start();
t4.start();
}
}
3、自旋锁(SpinLock)
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式获取锁
- 优点:减少线程上下文切换的消耗
- 缺点:循环会消耗cpu
public class SpinLockDemo {
//原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()
+"\t come in");
//第一次thread为null,执行下面代码以后thread变为thread,但是
//atomicReference.compareAndSet(null,thread)为true不执行循环
while (!atomicReference.compareAndSet(null,thread)){
}
}
public void myUnlock(){
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()
+"\t invoked myUnlock()");
}
public static void main(String[] args) throws InterruptedException {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(new Runnable() {
@Override
public void run() {
spinLockDemo.myLock();
try {
//AA持有锁5s
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5s后AA线程为null
spinLockDemo.myLock();
}
},"AA").start();
//保证了A线程先启动
TimeUnit.SECONDS.sleep(1);
new Thread(new Runnable() {
@Override
public void run() {
//thread不为null,一直循环比较thread是否为null,5s后AA线程为null
//BB拿到了thread,不执行循环
spinLockDemo.myLock();
//继续执行unlock
spinLockDemo.myUnlock();
}
},"BB").start();
}
}
代码过程为:
首先thread线程为null,执行
while (!atomicReference.compareAndSet(null,thread)) 方法,此时null则atomicReference.compareAndSet(null,thread) 为true,while判断为false,不执行循环,AA线程此刻为thread
同时BB线程开始执行,但是此刻thread线程已经不为null,所以while方法执行,一直在循环
直到5s后AA线程执行atomicReference.compareAndSet(thread,null); thread为null
则BB可以结束while循环,接着执行myUnlock方法
4、独占锁(写锁)/共享锁(读锁)/互斥锁
独占锁:一次只能被一个线程持有,Lock和Synchronized都是独占锁
共享锁:该锁可以同时被多个线程所持有,ReentrantReadWriteLock起读锁是共享的,写锁是独占锁
- 读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的
- 写操作:原子+独占,中间不能被分割,打断
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
public void put(String key,Object value) throws InterruptedException {
rwlock.writeLock().lock();
try {
System.out.println(Thread.currentThread()+"\t 正在写入 :"+ key);
TimeUnit.MILLISECONDS.sleep(300);
map.put(key, value);
System.out.println(Thread.currentThread()+"\t 写入完成 :"+ key);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwlock.writeLock().unlock();
}
}
public void get(String key) throws InterruptedException {
rwlock.readLock().lock();
try {
System.out.println(Thread.currentThread()+"\t 正在读取 :");
TimeUnit.MILLISECONDS.sleep(300);
Object result = map.get(key);
System.out.println(Thread.currentThread()+"\t 读取完成 :"+ result);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwlock.readLock().unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i=1;i<=5;i++){
final int temInt = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
myCache.put(temInt+"",temInt+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},String.valueOf(i)).start();
}
for (int i=1;i<=5;i++){
final int temInt = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
myCache.get(temInt+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},String.valueOf(i)).start();
}
}
}