紧接上篇的知识点Java——锁,讨论各种锁的具体实现方法。
1 synchronized关键字
对象锁
/**
* 为非公平锁
* 对象锁也叫方法锁,是针对一个对象实例的,它只在该对象的某个内存位置声明一个标识该对象是否拥有锁,
* 所有它只会锁住当前的对象,而并不会对其他对象实例的锁产生任何影响,不同对象访问同一个被synchronized修饰的方法的时候不会阻塞,
* 创建一个类,synchronized修饰普通方法,即为对象锁,那么这个时候,多个线程访问同一个对象实例的这个方法时,是会同步的,并且只有一个线程执行完,另一个线程才会执行。
*
*
* synchronized修饰的代码块传入this也属于对象锁
* 应用:减小锁粒度,第二种形式就比较好,比如A线程调用一个同步方法需要很长时间,那么B就要等待很长时间,这个时候可以将必须同步的代码使用synchronized代码块
*/
public class ObjectLockDemo {
private synchronized void method1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//synchronized修饰为同步方法,如果先调用method1,则4秒后才会调用method2
//如果不用synchronized修饰,则可以直接异步调用,没有影响
private void method2(){
System.out.println(Thread.currentThread().getName());
}
private void doLongTimeTask(){
try{
System.out.println("当前线程开始:"+Thread.currentThread().getName()+",正在执行一个较长时间的业务操作,其内容不需要同步");
Thread.sleep(10000);
synchronized (this){
System.out.println("当前线程:"+Thread.currentThread().getName()+",执行同步代码块,对其同步变量进行性操作");
Thread.sleep(1000);
}
System.out.println("当前线程结束:"+Thread.currentThread().getName()+",执行完毕");
}catch (Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
//创建一个对象
ObjectLockDemo myObject=new ObjectLockDemo();
//多个实例对象不会同步对象锁
ObjectLockDemo myObject2=new ObjectLockDemo();
Thread t1=new Thread (new Runnable() {
@Override
public void run() {
myObject.method1();
}
},"t1");
Thread t2=new Thread (new Runnable() {
@Override
public void run() {
myObject.method1();
}
},"t2");
t1.start();
t2.start();
Thread t3=new Thread(new Runnable() {
@Override
public void run() {
myObject.doLongTimeTask();
}
},"t3");
Thread t4=new Thread(new Runnable() {
@Override
public void run() {
myObject.doLongTimeTask();
}
},"t4");
t3.start();
t4.start();
}
}
类锁
/**
* 为非公平锁
*类锁实际上就是这个类的对象的对象锁,也就是说每个类都是一个java.lang.Class对象,类锁就是这个类的类对象锁。
* 类锁的三个获取方式
* 1、synchronized (xxx.class) {}
* 2、synchronized (Class.forName("xxx")) {...}
* 3、public static synchronized void method(){...},即修饰静态方法
* 这个时候无论线程调用的是多少个对象实例的方法,都会同步
*/
public class ClassLockDemo {
public static synchronized void method1(){
try{
System.out.println(Thread.currentThread().getName());
Thread.sleep(4000);
System.out.println("method1方法执行完毕");
}catch (Exception e){
e.printStackTrace();
}
}
public void method2(){
System.out.println("method2");
}
public static void main(String[] args) {
//创建一个对象
ClassLockDemo myObject1=new ClassLockDemo();
//多个实例方法均会同步类锁
ClassLockDemo myObject2=new ClassLockDemo();
Thread t1=new Thread (() -> myObject1.method1(),"t1");
Thread t2=new Thread (new Runnable() {
@Override
public void run() {
myObject2.method1();
}
},"t2");
t1.start();
t2.start();
}
}
2 接口锁
Lock接口
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* lock锁默认为非公平锁,ReentrantLock类有构造法方法设置为公平锁或非公平锁,具体为ReentrantLock(boolean)
* Lock接口有6个方法
* 1、void lock() 获取锁
* 2、void lockInterruptibly() 如果当前线程未被中断,则获取锁
* 3、Condition new Condition() 返回绑定到此Lock实例的新Condition实例
* 4、boolean tryLock() 仅在调用时锁为空闲状态才获取该锁
* 5、boolean tryLock(long time,TimeUnit unit) 如果锁在给定的等待事件内空闲,并且当前线程未被中断,则获取锁
* 6、void unlock() 释放锁
*
* 与synchronized关键字对比,Lock接口的使用要注意一下几点:
* 1、synchronized是Java语言的关键字,因此是内置特性,Lock不是Java语言内置的,Lock是一个接口,通过实现类可以实现同步访问。
* 2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,
* 但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
* 3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,
* 但是ReetrantLock的性能能维持常态。
*/
public class LockDemo {
private Lock lock = new ReentrantLock();
public void method1() {
lock.lock();
try {
System.out.println("线程 " + Thread.currentThread().getName() + " method1开始执行");
Thread.sleep(10000);
System.out.println("线程 " + Thread.currentThread().getName() + " method1开始结束");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("线程 " + Thread.currentThread().getName() + " 释放线程锁");
}
}
public static void main(String[] args) {
//创建一个对象
LockDemo myObject1 = new LockDemo();
//多个实例方法均会同步类锁
LockDemo myObject2 = new LockDemo();
Thread t1 = new Thread(() -> myObject1.method1(), "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
myObject1.method1();
}
}, "t2");
t1.start();
t2.start();
}
}
自适应锁
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
public class CustomizedSpinningLock {
/**
* 持有锁的线程,null表示锁未被线程持有
*/
private AtomicReference<Thread> ref = new AtomicReference<>();
public void lock() {
Thread currentThread = Thread.currentThread();
while (!ref.compareAndSet(null, currentThread)) {
//当ref为null的时候compareAndSet返回true,反之为false
//通过循环不断的自旋判断锁是否被其他线程持有
}
}
public void unLock() {
Thread cur = Thread.currentThread();
if (ref.get() != cur) {
//exception ...
}
ref.set(null);
}
}
public class TestLock {
static int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
CountDownLatch countDownLatch = new CountDownLatch(100);
CustomizedSpinningLock customizedSpinningLock = new CustomizedSpinningLock();
for (int i = 0; i < 100; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行");
customizedSpinningLock.lock();
++count;
//JDK1.6以后出现了自适应自旋锁,删除该语句可明显体现此特性
Thread.sleep(1000);
customizedSpinningLock.unLock();
countDownLatch.countDown();
}catch (Exception e){
e.printStackTrace();
}
}
});
}
countDownLatch.await();
System.out.println(count);
executorService.shutdown();
}
}
ReadWriteLock接口
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* ReadWriteLock 接口只有两个方法:
* 1、Lock readLock() 返回用于读取操作的锁,仅用于标记,属于共享锁
* 2、Lock writeLock() 返回用于写入操作的锁,仅用于标记,属于排他锁
*
* 只要没有 writer,读取锁可以由多个 reader 线程同时保持,而写入锁是独占的。
*/
public class ReadWriteLockDemo {
//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
private Object data = null;
ReadWriteLock lock = new ReentrantReadWriteLock();
// 读数据
public void readMethod() {
// 加读锁
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + " have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放读锁
lock.readLock().unlock();
}
}
// 写数据
public void writeMethod(Object data) {
// 加写锁
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep(10000);
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放写锁
lock.writeLock().unlock();
}
}
public static void main(String[] args) {
ReadWriteLockDemo queue = new ReadWriteLockDemo();
//启动1个读线程
new Thread() {
public void run() {
while(true) {
queue.readMethod();
}
}
}.start();
//启动1个写线程
new Thread() {
public void run() {
while(true) {
queue.writeMethod(new Random().nextInt(10000));
}
}
}.start();
//启动1个读线程
new Thread() {
public void run() {
while(true) {
queue.readMethod();
}
}
}.start();
}
}
参考自:
新增:原子类的线程安全演示
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
public void CASMethod(AtomicInteger atomicInteger) {
System.out.println("线程 " + Thread.currentThread().getName() + " 原子类自增的数据为 " + atomicInteger.incrementAndGet());
}
public void NoCASMethod(int a) {
a++;
System.out.println("线程 " + Thread.currentThread().getName() + " 非原子类自增的数据为 " + a);
}
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
CountDownLatch countDownLatch = new CountDownLatch(10);
AtomicInteger atomicInteger = new AtomicInteger(0);
int count = 0;
CASDemo casDemo = new CASDemo();
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行");
casDemo.CASMethod(atomicInteger);
casDemo.NoCASMethod(count);
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
countDownLatch.await();
executorService.shutdown();
}
}
执行结果:
线程 pool-1-thread-1 正在执行
线程 pool-1-thread-1 原子类自增的数据为 1
线程 pool-1-thread-1 非原子类自增的数据为 1
线程 pool-1-thread-2 正在执行
线程 pool-1-thread-2 原子类自增的数据为 2
线程 pool-1-thread-2 非原子类自增的数据为 1
线程 pool-1-thread-3 正在执行
线程 pool-1-thread-3 原子类自增的数据为 3
线程 pool-1-thread-3 非原子类自增的数据为 1
线程 pool-1-thread-4 正在执行
线程 pool-1-thread-4 原子类自增的数据为 4
线程 pool-1-thread-4 非原子类自增的数据为 1
线程 pool-1-thread-5 正在执行
线程 pool-1-thread-5 原子类自增的数据为 5
线程 pool-1-thread-5 非原子类自增的数据为 1
线程 pool-1-thread-6 正在执行
线程 pool-1-thread-6 原子类自增的数据为 6
线程 pool-1-thread-6 非原子类自增的数据为 1
线程 pool-1-thread-7 正在执行
线程 pool-1-thread-7 原子类自增的数据为 7
线程 pool-1-thread-7 非原子类自增的数据为 1
线程 pool-1-thread-8 正在执行
线程 pool-1-thread-8 原子类自增的数据为 8
线程 pool-1-thread-8 非原子类自增的数据为 1
线程 pool-1-thread-9 正在执行
线程 pool-1-thread-9 原子类自增的数据为 9
线程 pool-1-thread-9 非原子类自增的数据为 1
线程 pool-1-thread-10 正在执行
线程 pool-1-thread-10 原子类自增的数据为 10
线程 pool-1-thread-10 非原子类自增的数据为 1
Process finished with exit code 0