多线程2(Lock 显示锁)

五、Lock 显示锁

在JDK5中增加了Lock锁接口,有ReentrantLock实现类,ReentrantLock锁称为可重入锁,它功能比synchronized 多。

5.1 锁的可重入性

锁的可重入是指,当一个线程获得一个对象锁后,再次请求该对象锁时是可以获得该对象的锁的。

/**
 * 演示锁的可重入性
 */
public class Test01 {
   
    public synchronized void sm1 () {
   
        System.out.println("同步方法1");
        // 线程执行sm1 方法,默认this作为锁对象,在sm1 方法中调用了sm2 方法,注意当前线程还是持有this锁对象的
        // sm2 同步方法默认的锁对象也是this对象,要执行sm2 必须先获得this 锁对象,当前this 对象被当前线程持有,
        // 可以再次获得this对象,这就是锁的可重入性。假设,锁不可重入的话,可能会造成死锁。
        sm2();
    }
    private synchronized void sm2 () {
   
        System.out.println("同步方法2");
        sm3();
    }
    private synchronized void sm3 () {
   
        System.out.println("同步方法3");
    }

    public static void main(String[] args) {
   
        Test01 test01 = new Test01();
        new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                test01.sm1();
            }
        }).start();
    }
}

5.2 ReentrantLock

5.2.1 ReentrantLock 的基本使用

调用lock方法获得锁,调用unlock 方法释放锁。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Test02 {
   
    // 定义显示锁
    static Lock lock =  new ReentrantLock();

    // 定义方法
    public static void sm() {
   
        // 先获得锁
        lock.lock();
        // for 循环就是同步代码块
        for (int i = 0; i < 100; i++) {
   
            System.out.println(Thread.currentThread().getName() + "---"+ i);

        }
        // 释放锁
        lock.unlock();
    }

    public static void main(String[] args) {
   
        Runnable r = new Runnable() {
   
            @Override
            public void run() {
   
                sm();
            }
        };
        // 启动3个线程
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }

}

多线程2(Lock 显示锁)_第1张图片
使用lock锁同步不同方法中的同步代码块

/**
 * 使用lock锁同步不同方法中的同步代码块
 */
public class Test03 {
   

    static Lock lock = new ReentrantLock();// 定义锁对象
    public static void sm1() {
   
        // 经常在try代码块中获得 lock 锁,经常在finally子句中释放锁
        try{
   
            lock.lock(); // 获得锁
            System.out.println(Thread.currentThread().getName() + "-- method 1"+ System.currentTimeMillis());
            Thread.sleep(new Random().nextInt(1000));
            System.out.println(Thread.currentThread().getName() + "-- method 1"+ System.currentTimeMillis());
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        } finally {
   
            lock.unlock(); // 释放锁
        }
    }

    public static void sm2() {
   
        // 经常在try代码块中获得 lock 锁,经常在finally子句中释放锁
        try{
   
            lock.lock(); // 获得锁
            System.out.println(Thread.currentThread().getName() + "-- method 2"+ System.currentTimeMillis());
            Thread.sleep(new Random().nextInt(1000));
            System.out.println(Thread.currentThread().getName() + "-- method 2"+ System.currentTimeMillis());
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        } finally {
   
            lock.unlock(); // 释放锁
        }
    }

    public static void main(String[] args) {
   
        Runnable r1 = new Runnable() {
   
            @Override
            public void run() {
   
                sm1();
            }
        };
        Runnable r2 = new Runnable() {
   
            @Override
            public void run() {
   
                sm2();
            }
        };
        new Thread(r1).start();
        new Thread(r1).start();
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r2).start();
        new Thread(r2).start();
    }

}

多线程2(Lock 显示锁)_第2张图片
ReentrantLock 锁的可重入性

/**
 *  ReentrantLock 锁的可重入性
 */
public class Test04 {
   
    static class Subthread extends Thread {
   
        private static Lock lock = new ReentrantLock(); // 定义锁对象

        public static int num = 0; // 定义变量

        @Override
        public void run() {
   
            for (int i = 0; i < 10000; i++) {
   

                try{
   
                    // 可重入锁指可以反复获得该锁
                    lock.lock();
                    lock.lock();
                    num++;
                } finally {
   
                    lock.unlock();
                    lock.unlock();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        Subthread t1 = new Subthread();
        Subthread t2 = new Subthread();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(Subthread.num);
    }
}

多线程2(Lock 显示锁)_第3张图片

5.2.3 lockInterruptibly() 方法的使用

lockInterruptibly() 方法的作用:如果当前线程未被中断则获得锁,如果当前线程被中断则出现异常

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * lockInterruptibly() 方法的作用: 如果当前线程未被中断则获得锁,如果当前线程被中断则出现异常.
 */
public class Test05 {
   

    static class Servier {
   
        private Lock lock = new ReentrantLock(); // 定义锁对象

        public void serviceMethod(){
   
            try {
   
                // lock.lock(); // 获得锁定,即使调用了线程的interrupt()方法,他也没有真正的中断线程
                lock.lockInterruptibly(); // 如果线程被中断了,不会获得锁,会产生异常;
                System.out.println(Thread.currentThread().getName() +" -- begin lock");
                // 执行一段耗时的操作
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
   
                    new StringBuilder();
                }
                System.out.println(Thread.currentThread().getName() + "--end lock");
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            } finally {
   
                lock.unlock(); // 释放锁
                System.out.println(Thread.currentThread().getName() +"-- 释放锁");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
   
        Servier servier = new Servier();
        Runnable runnable = new Runnable() {
   
            @Override
            public void run() {
   
                servier.serviceMethod();
            }
        };

        Thread t1 = new Thread(runnable);
        t1.start();
        Thread.sleep(50);
        Thread t2 = new Thread(runnable);
        t2.start();
        Thread.sleep(50);
        t2.interrupt(); // 中断t2 线程
    }
}

运行结果:
多线程2(Lock 显示锁)_第4张图片
多线程2(Lock 显示锁)_第5张图片
对于synchronized 内部锁来说,如果一个线程在等待锁,只有两个结果:要么该线程获得锁继续执行,要么就继续等待。
对于ReentrantLock 可重入锁来说,提供另外一种可能,在等待锁的过程中,程序可以根据需要取消对锁的请求。

lockInterruptibly() 解决死锁问题

import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;

/**
 *  通过ReentrantLock 锁的 lockInterruptibly(); 方法避免死锁的产生.
 */
public class Test06 {
   
    static class IntLock implements Runnable {
   
        public static ReentrantLock lock1 = new ReentrantLock();
        public static ReentrantLock lock2 = new ReentrantLock();
        int lockNum; // 定义整数变量,决定使用哪个锁.
        public IntLock(int lockNum) {
   
            this.lockNum = lockNum;
        }

        @Override
        public void run() {
   
            try {
   
                if (lockNum %2 == 1) {
    // 奇数,先锁1,再锁2
//                    lock1.lock();
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁1,还需要获得锁2");
                    Thread.sleep(new Random().nextInt(500));
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() +"获得了锁1与锁2>>>");
                } else {
    // 偶数, 先锁2 再锁1
                    lock2.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() + "获得锁2,还需要获得锁1");
                    Thread.sleep(new Random().nextInt(500));
                    lock1.lockInterruptibly();
                    System.out.println(Thread.currentThread().getName() +"获得了锁1与锁2>>>");
                }
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            } finally {
   
                if(lock1.isHeldByCurrentThread()) {
    // 判断当前线程是否持有该锁,如果持有就释放锁
                    lock1.unlock();
                }
                if (lock2.isHeldByCurrentThread()) {
   
                    lock2.unlock();
                }
                

你可能感兴趣的:(java,#,java多线程,java)