1. java中常用的独占锁有两种:Synchronized,ReenTrantLock。两种锁在性能上差别不大,但ReenTrantLock手动加锁,解锁,更加灵活,功能更加丰富
  2. 独占锁,可重入锁

    public class TestReenTrantLock {
    
     static Lock lock = new ReentrantLock(true);
    
     public static void main(String[] args) {
         //可重入锁:一个线程多次获取同一个对象上的锁。可重入锁的意义之一在于防止死锁 
         实现原理:当线程获取锁时,jvm将记录锁的占有者,并将锁中的计数器加1,
    同一个线程再次获得锁,计数器再次加1。未被占用的锁,计数器为0。
    当线程退出同步块,计数器值将递减,直到计数器值为0,锁被释放。其他线程才能获得锁
         独占锁:一个锁一次只能被一个线程占有
         method();
         method1();
    
         lock.unlock();
         lock.unlock();
     }
    
    
     public static void method(){
         lock.lock();
         System.out.println("第一次获取锁");
     }
    
     public static void method1(){
         lock.lock();
         System.out.println("第二次获取锁");
     }
    }

    这里在method中获取了TestReenTrantLock类锁,调用method1时再一次获取了TestReenTrantLock类锁,这就是可重入锁

  3. 可以实现公平锁
public class FairReenTrantLock {

    /**
     * 公平锁:当锁可用时,在锁上等待时间最长的线程获取锁的使用权
     * 无参数或为fasle,为非公平锁
     * 非公平锁:随机获得锁的使用权
     */
    static Lock lock = new ReentrantLock(true);

    public static void main(String[] args) {
        for (int i=0; i<5; i++){
            new Thread(new ThreadDemo()).start();
        }
    }


    static class ThreadDemo implements Runnable{

        public ThreadDemo(){

        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(2);
            }catch (Exception e){
                e.printStackTrace();
            }
            for(int i=0; i<2; i++){
                lock.lock();
                System.out.println("获得锁的线程"+Thread.currentThread().getName());
                lock.unlock();
            }
        }
    }

}


  1. 可以响应线程中断

    首先了解一下 public void interrupt();(属于ThreadL类)
    image.png

public class TestInterrupt {

    public static void main(String[] args) throws InterruptedException {
        Thread testThread = new TestThread();
        testThread.start();
        testThread.interrupt();
    }

    /*
    *下面测试的三个方法都会阻塞线程,如果没有使用这个三个方法,线程会直接走完,
    *而且isInterrupted()为true
    *使用了其中一个,会抛出InterruptedException,isInterrupted()为false
    */
    static class TestThread extends Thread {

        @Override
        public void run() {
            System.out.println("线程开始运行");
            try {
                //TimeUnit.SECONDS.sleep(5);
                //wait();
                join();
            }catch (Exception e){
                System.out.println("线程发生异常"+e);
                System.out.println(Thread.currentThread().isInterrupted());
            }
            System.out.println("线程结束运行");
        }
    }
}
  • 响应中断案例
public class LockInterruptibly {

    static Lock firstLock = new ReentrantLock();
    static Lock secondLock = new ReentrantLock();

    public static void main(String[] args) {
        Thread firstthread = new Thread(new ThreadDemo(firstLock,secondLock));
        Thread secondThread = new Thread(new ThreadDemo(secondLock,firstLock));
        firstthread.start();
        secondThread.start();
        firstthread.interrupt();
    }

    static class ThreadDemo implements Runnable {
        private Lock firstLock;
        private Lock secondLock;

        public ThreadDemo(Lock firstLock,Lock secondLock){
            this.firstLock = firstLock;
            this.secondLock = secondLock;
        }

        //会中断处于等待的线程,释放锁,然后另一个线程获取锁,继续走
        @Override
        public void run() {
            try {
                firstLock.lockInterruptibly();
                TimeUnit.SECONDS.sleep(1);
                secondLock.lockInterruptibly();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                firstLock.unlock();
                secondLock.unlock();
                System.out.println(Thread.currentThread().getName()+"正常结束");
            }
        }
    }
}
  • 不响应中断案例
public class TestSynchronized {

    static Object resource1 = new Object();
    static Object resource2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new TestSynchronizedThread(resource1,resource2);
        Thread thread2 = new TestSynchronizedThread(resource2,resource1);

        thread1.start();
        thread2.start();
        thread1.interrupt();//中断线程,
    }

    static class TestSynchronizedThread extends Thread {
        Object resource1;
        Object resource2;
        public TestSynchronizedThread(Object resource1,Object resource2){
            this.resource1 = resource1;
            this.resource2 = resource2;
        }

        //即使跑出异常,也不会中断线程,两个线程继续互相等待资源,造成死锁
        @Override
        public void run() {
            synchronized (resource1){
                System.out.println(Thread.currentThread().getName()+"--111");
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName()+"--222");
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()+"--333");
                }

                System.out.println(Thread.currentThread().getName()+"--444");
                synchronized (resource2){
                    System.out.println(555);
                }
            }

        }
    }
}
  1. 获取锁时,限时等待
public class TestTryLock {

    static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
       Thread thread =  new Thread() {
            @Override
            public void run() {
                if(lock.tryLock()){//获取锁的结果立即返回,true为成功获取锁,false表示失败
                    System.out.println("线程获取到锁");
                }

                try {
                    if(lock.tryLock(5, TimeUnit.SECONDS)){
                        System.out.println("为了获取锁,最多等待5s");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        thread.start();
    }
}
  1. 利用condition实现等待通知机制,
public class TestCondition {

    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        lock.lock();
        System.out.println("主线程获取锁");
        new Thread(new TestConditionThread()).start();
        condition.await();//主线程等待,释放锁
        System.out.println("主线程被唤醒");
        lock.unlock();
    }


   //使用Condition之前必须先获取锁,调用condition的await()使当前线程释放锁,处于等待状态
   //直到有其他线程调用同一个condition的signal()才被唤醒
   static class TestConditionThread extends Thread {
        @Override
        public void run() {
            System.out.println("启动子线程"+Thread.currentThread().getName());
            lock.lock();//子线程获取锁
            System.out.println("子线程获取到锁");
            condition.signal();//子线程释放锁,唤醒主线程
            lock.unlock();
        }
    }
}
  1. condition实现阻塞队列
public class TestBlockingQueue {

    private int size;

    private ReentrantLock lock = new ReentrantLock();

    Condition full = lock.newCondition();
    Condition empty = lock.newCondition();

    private LinkedList<Integer> list = new LinkedList<>();

    public TestBlockingQueue(int size){
        this.size = size;
    }


    /**
     * 利用ReentrantLock实现阻塞队列,队列满时,入队阻塞
     * @param data
     */
    public void enqueue(Integer data){
        try {
            lock.lock();
            while (list.size() == size){
                full.await();//队列满了,阻塞当前线程,阻止入队
            }
            list.add(data);
            System.out.println("入队成功"+data);
            empty.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }

    public void dequeue(){
        try {
            lock.lock();
            while(list.size() == 0){
                empty.await();//队列为空,阻塞出队
            }
            Integer e = list.removeFirst();
            System.out.println("出队成功"+e);
            full.signal();
        }catch (Exception e){

        }finally{
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        TestBlockingQueue testBlockingQueue = new TestBlockingQueue(3);
        for(int i=0; i<9; i++){
            int data = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    testBlockingQueue.enqueue(data);
                }
            }).start();
        }

        for(int i=0; i<5; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    testBlockingQueue.dequeue();
                }
            }).start();
        }

    }
}
  1. synchronized

    作用:解决多个线程之间访问资源时线程安全问题,保证被它修饰的代码,同一时刻只有一个线程访问。

    重量级锁:监视器锁(monitor)依赖底层操作系统的Mutex Lock来实现,java线程是映射在操作系统原生线程之上的。因此挂起或唤醒一个线程,需要操作系统帮忙。操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要很长时间,耗时成本较高。但是java6以后对synchronized做了大量的优化

    Synchronized的使用方式
    1)修饰普通方法给当前实例对象加锁,想要进入方法需要先获取当前实例对象的锁,Synchronized(this)
    2)修饰静态方法,给类对象加锁,想要进入方法需要先获取类对象的锁Synchronized(class),实例对象锁和类锁并不互斥,互相没有影响
    3)修饰代码块,对给定的对象加锁,可以是实例对象,也可以是类对象Synchronized(this|object)
    4)尽量不要使用Synchronized(Stirng)因为jvm中字符串常量池具有缓存功能
    5)如果类中存在共享成员变量,即使方法加了Synchronized,也会出现线程安全问题,因为Synchronized锁住的方法和代码片段,不会锁住属性

public class SynchronizedTest {

    List list = Collections.synchronizedList(new ArrayList<>());
    
    //A,B线程执行这个方法
    public synchronized Boolean putIfAbsent(Integer i){
        //A线程执行完这一步,B线程开始执行,B执行完成后,A再次执行,这时会重复添加元素
        boolean flag = !list.contains(i);
        if(flag){
            list.add(i);
        }
        return flag;
    }
}

forest
0 声望1 粉丝