1. synchronized
他的工作原理,当一个线程进入使用“synchronized”修饰的方法或者代码块时,他会获取锁,如果锁被另一个线程所占用,那么改线程就会处于等阻塞状态,知道锁被释放。一旦线程完成这个代码块或者方法时,他就会释放锁,这样其他线程就可以获取到锁心执行代码块了
他是独占锁,也是非公平锁
在方法中声明,可以确保这个方法在同一时间只有一个线程执行此方法,
public synchronized void synchronizedMethod() {
// 线程安全的操作
}
//synchronized锁的是对象,千万不能再线程里面new要执行的对象,
//要让两个线程同时抢一个对象的锁,而不是,像个线程去new对象
//线程里面new的对象是互不干扰的
Main main = new Main();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 20; i++) {
System.out.println("线程一被执行第:" + i + "次" + main.aaa());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 20; i++) {
System.out.println("线程二被执行第:" + i + "次" + main.aaa());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread1.start();
thread2.start();
2.可重入锁,ReentrantLock
简单理解就是方法一,有一个锁,方法二也有一个锁,但是在执行时,方法一调用了方法二,当进入方法一时,也就进了方法二的锁,他是独占锁
使用场景:①递归函数,在递归过程中多次获取同一个锁。②线程安全的数据结构,当一个线程在对另一个线程的数据结构进行操作时,可能需要多次获取同一个锁来保证操作的原子性③线程之间的协作,在一些需要线程间的协作的场景中,可重入锁可以确保一个线程在持有锁的情况下再洗获取锁,以免其他线程干扰,④资源的分配与释放没在西苑分配与释放的场景中,需要多次获取同一个锁来保证资源的正确释放。
//创建一个私有的可重入的锁
private final Lock lock = new ReentrantLock();
//创建一个计数器
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
System.out.println("调用者++++++++++ to: " + count);
decrement(); // 调用另一个方法,测试重入性
} finally {
lock.unlock(); // 释放锁
}
}
public void decrement() {
lock.lock(); // 获取锁
try {
count--;
System.out.println("被调者------ to: " + count);
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
Main example = new Main();
// 启动多个线程来增加计数
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 3; j++) {
example.increment();
}
}).start();
}
3.自旋锁
指尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试获取锁,这样的好处是减少了上下文切换的消耗,确定是循环会消耗CPU
。循环比较直到成功为止。一句话“死循环获取到代码的执行权限”。
自旋锁的应用场景:银行的叫号就可以利用自旋锁,四台机器同时点击叫号,只有一个线程可以拿到号码。
4. ReadWriteLock 读写锁
读锁:只能可能被多个线程所占有。
写锁:只能被一个线程所占有,对ReentrantLock
和Synchronized
而言都是独占锁。
public class SharedResource {
private int resource;
//创建一个读写锁的实例
private final ReadWriteLock lock = new ReentrantReadWriteLock();
//写
public void write(int value) {
//获取写锁
lock.writeLock().lock();
try {
System.out.println("写入的值: " + value);
resource = value;
} finally {
//释放锁
lock.writeLock().unlock();
}
}
//读
public int read() {
//获取读锁
lock.readLock().lock();
try {
System.out.println(" 读出来的值: " + resource);
return resource;
} finally {
lock.readLock().unlock();
}
}
}
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();
Runnable writer = () -> {
for (int i = 0; i < 6; i++) {
sharedResource.write(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable reader = () -> {
for (int i = 0; i < 6; i++) {
sharedResource.read();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread writerThread = new Thread(writer);
Thread readerThread1 = new Thread(reader);
Thread readerThread2 = new Thread(reader);
writerThread.start();
readerThread1.start();
readerThread2.start();
}
结果
读写锁这种那份机制可以在读取操作频繁且写入操作较少的情况下提高并发性能,多个线程在读取公共资源时,减少了读操作的竞争和阻塞,同时在写操作时需要获取写锁进行独占资源,读写操作时互斥的,可以保证在写的操作期间,不会有其他线程进行读操作或者写操作,保证了数据的一致性和可靠性。