Java——锁的实现

紧接上篇的知识点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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值