最近一直在搞并发,但是都是偏于理论和证明的方面,代码写的少之又少,今天主要讲一些并发的代码书写。具体会从通过多个Demo来加深你对并发的理解。
-
synchronized关键字
/* * synchronized关键字 * synchronized关键字锁定的是对象不是代码块,demo中锁的是object对象的实例 * 锁定的对象有两种:1.类的实例,2.类对象(类锁) * 加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一 * */ public class Demo1 { private int count = 10; private Object object = new Object(); public void test1() { synchronized (object) { count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } } public void test2() { //synchronized(this)锁定是当前类的实例,这里锁定的是Demo1的实例 synchronized (this) { count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } } //直接加在方法声明上,相当于是synchronized(this) public synchronized void test3() { count --; System.out.println(Thread.currentThread().getName() + " count = " + count); } //synchronized关键字修饰静态方法锁定的是类对象(类的.class文件) //静态方法中synchronized锁定代码块,锁定的对象不能是类的实例,只能是类对象(类的.class文件) public synchronized static void test4() { count--; System.out.println( "test1 count = " + count); } }
-
锁的的一些使用规则
import java.util.concurrent.TimeUnit; /* * 锁对象的改变 * 锁定某对象object,如果object的属性发生改变,不影响锁的使用 * 但是如果object变成了另一个对象,则锁定的对象发生改变 * 应该避免将锁定对象的引用变成另一个对象。 * */ public class Demo1 { Object object = new Object(); public void test() { synchronized (object){ while (true) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } public static void main(String[] args) { Demo1 demo = new Demo1(); new Thread(demo::test, "t1").start(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } Thread t2 = new Thread(demo::test, "t2"); demo.object = new Object(); t2.start(); } }
import java.util.concurrent.TimeUnit; /* * 不要以字符串常量作为锁定的对象 * 在下面,test1和test2其实锁定的是同一个对象,都是存在字符串的常量池中 * */ public class Demo2 { String s1 = "hello"; String s2 = "hello"; public void test1() { synchronized (s1){ System.out.println("t1 start..."); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t1 end..."); } } public void test2() { synchronized (s2){ System.out.println("t2 start..."); } } public static void main(String[] args) { Demo2 demo2 = new Demo2(); new Thread(demo2::test1, "test1").start(); new Thread(demo2::test2, "test2").start(); } }
import java.util.concurrent.TimeUnit; /* * 同步代码块中的语句越少越好 * 比较test1和test2 * 业务逻辑中只有count++这句需要sync,这时不应该给整个方法上锁 * 采用细粒度的锁,可以使线程争用时间变短,从而提高效率 * */ public class Demo3 { int count = 0; public synchronized void test1() { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } count++; try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } public void test2(){ try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (this) { count++; } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
线程重入问题
/* * 问题就在于线程重入的问题 * 第一个线程减了个1变成9,还没有打印,第二个线程又减了1,第三个线程又减了个1, * 这时候虽然第一个线程只减了一个1但是却打印出来一个7(这里的情况是不一定的) * 可以给方法加上synchronized * */ public class Demo1 implements Runnable { private int count = 10; @Override public synchronized void run() { count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } public static void main(String[] args) { Demo1 demo = new Demo1(); for (int i = 0; i < 5; i++) { new Thread(demo, "THREAD" + i).start(); } } }
public class Demo2 implements Runnable { private int count = 10; @Override public void run() { count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } public static void main(String[] args) { for (int i = 0; i < 5; i++) { //相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自标记锁标记,可以同时执行。 Demo2 demo2 = new Demo2(); new Thread(demo2, "THREAD" + i).start(); } } }
-
面试题
//同步方法和非同步方法是否可以同时调用? 可以 public class Demo { public synchronized void test1() { System.out.println(Thread.currentThread().getName() + " test1 start..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " test1 end ..."); } public void test2() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " test2"); } public static void main(String[] args) { Demo demo = new Demo(); new Thread(demo::test1, "test1").start(); new Thread(demo::test2, "test2").start(); } }
-
脏读问题
import java.util.concurrent.TimeUnit; /* * 脏读问题 * 实际业务当中应该看是否允许脏读,不允许的情况下对脏读方法也要加锁 * */ public class Demo { String name; double balance; public synchronized void set(String name, double balance) { this.name = name; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } this.balance = balance; } public /*synchronized*/ double getBalance(String name) { return this.balance; } public static void main(String[] args) { Demo demo = new Demo(); new Thread(() -> demo.set("yesheng", 100.0)).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(demo.getBalance("yesheng")); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(demo.getBalance("yesheng")); } }
-
面试题
import java.util.concurrent.TimeUnit; //一个同步方法调用另一个同步方法,能否得到锁?可以,synchronized本身可支持重入 public class Demo { synchronized void test1() { System.out.println("test1 start..."); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test2(); } synchronized void test2() { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test2 start..."); } public static void main(String[] args) { Demo demo = new Demo(); demo.test1(); } }
-
重入锁
import java.util.concurrent.TimeUnit; //这里是重入锁的另一种情况,继承 public class Demo { synchronized void test() { System.out.println("demo test start..."); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("demo test end...."); } public static void main(String[] args) { new Demo2().test(); } } class Demo2 extends Demo{ @Override synchronized void test() { System.out.println("demo2 test start...."); super.test(); System.out.println("demo2 test end...."); } }
-
异常没有处理的情况
import java.util.concurrent.TimeUnit; public class Demo { int count = 0; synchronized void test() { System.out.println(Thread.currentThread().getName() + "start..."); while (true) { count++; System.out.println(Thread.currentThread().getName() + " count = " + count); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if (count == 5) { //碰到异常的情况,如果没有处理,会自动释放锁,所以T2可以执行。 int i = 1 / 0; } } } public static void main(String[] args) { Demo demo = new Demo(); new Thread(demo::test, "t1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(demo::test, "t2").start(); } }
-
volatile关键字
/* * volatile 关键字,使一个变量在多个线程间可见 * main,t1线程都用到一个变量,java默认是t1线程中保留一份副本,这样如果main线程修改了该变量, * t1线程未必知道 * * 使用volatile关键字,会让所有线程都会读到变量的修改值 * * 在下面的代码中,running是存在于堆内存t1对象中 * 当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个副本, * 并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行 * * */ public class Demo { volatile boolean running = true; public void test() { System.out.println("test start..."); while (running) { } System.out.println("test end..."); } public static void main(String[] args) { Demo demo = new Demo(); new Thread(demo::test, "t1").start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } demo.running = false; } }
import java.util.ArrayList; import java.util.List; /* * 比如说第一个线程加到100了,还没往上加,另外一个线程来了,把100拿过来执行方法, * 然后第一个线程继续加到101,第二个线程也加到101,他两往回写都是101,线程不会管你加到哪儿了, * 虽然说加了2,但是实际上只加了1 * volatile并不能多个线程共同修改running变量锁带来的不一致问题 * 也就是说volatile不能替代synchronized或者说volatile保证不了原子性 * */ public class Demo { volatile int count = 0; public /*synchronized*/ void test() { for (int i = 0; i < 10000; i++) { count++; } } public static void main(String[] args) { Demo demo = new Demo(); List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 10; i++) { threads.add(new Thread(demo::test, "thread- " + i)); } threads.forEach(Thread::start); threads.forEach(o->{ try { o.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(demo.count); } }
import java.util.ArrayList; import java.util.List; public class Demo { int count = 0; //相比较上一个例子,synchronized既保证了原子性又保证了可见性 public synchronized void test() { for (int i = 0; i < 10000; i++) { count++; } } public static void main(String[] args) { Demo demo = new Demo(); List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 10; i++) { threads.add(new Thread(demo::test, "thread-" + i)); } threads.forEach(Thread::start); threads.forEach(o->{ try { o.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(demo.count); } }
-
面试题
import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; //一道面试题:多个atomic类连续调用能否构成原子性? public class Demo { AtomicInteger count = new AtomicInteger(); //比如count加到999了,这时候一个线程拿到count判断,虽然get方法保证原子性,但是他阻止 //不了其他线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了1 public void test() { for (int i = 0; i < 10000; i++) { if (count.get() < 1000) { count.incrementAndGet(); } } } public static void main(String[] args) { Demo demo = new Demo(); List<Thread> threads = new ArrayList<>(); for (int i = 0; i < 100; i++) { threads.add(new Thread(demo::test, "thread-" + 1)); } threads.forEach(Thread::start); threads.forEach(o-> { try { o.join(); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println(demo.count); } }
-
面试题:实现一个容器,提供两个方法,add size写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束线程2
-
版本一
import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /* * 一道面试题:实现一个容器,提供两个方法,add size * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数 * 当个数到5个时,线程2给出提示并结束线程2 * * 这里list在两个线程之间不保证可见性,所以线程2始终结束不了 * */ public class Container1 { List<Object> lists = new ArrayList<>(); public void add(Object o) { lists.add(o); } public int size() { return lists.size(); } public static void main(String[] args) { Container1 c = new Container1(); new Thread(() -> { for (int i = 0; i < 10; i++) { c.add(new Object()); System.out.println("add " + i); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1").start(); new Thread(() -> { while (true) { if (c.size() == 5) { break; } } System.out.println("t2线程结束"); }, "t2").start(); } }
-
版本二:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /* * 一道面试题:实现一个容器,提供两个方法,add size * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数 * 当个数到5个时,线程2给出提示并结束线程2 * * 有两个问题,第一由于没有加同步,可能size等于5的时候,有另外一个线程加了一下才break,不是很精确 * 第二个问题就是浪费CPU,T2线程用的死循环 * */ public class Container2 { volatile List<Object> lists = new ArrayList<>(); public void add(Object o) { lists.add(o); } public int size() { return lists.size(); } public static void main(String[] args) { Container2 c = new Container2(); new Thread(() -> { for (int i = 0; i < 10; i++) { c.add(new Object()); System.out.println("add " + i); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1").start(); new Thread(() -> { while (true) { if (c.size() == 5) { break; } } System.out.println("t2线程结束"); }, "t2").start(); } }
-
版本三:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /* * 一道面试题:实现一个容器,提供两个方法,add size * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数 * 当个数到5个时,线程2给出提示并结束线程2 * * 这里虽然T2能够及时收到消息唤醒,但是wait会释放锁,notify不会释放锁,所以T1线程结束后 * T2线程才执行完成 * */ public class Container3 { volatile List<Object> lists = new ArrayList<>(); public void add(Object o) { lists.add(o); } public int size() { return lists.size(); } public static void main(String[] args) { Container3 c = new Container3(); Object lock = new Object(); new Thread(() -> { synchronized (lock) { System.out.println("t2启动"); if (c.size() != 5) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("t2结束"); } }, "t2").start(); new Thread(() -> { System.out.println("t1启动"); synchronized (lock) { for (int i = 0; i < 10; i++) { c.add(new Object()); System.out.println("add" + i); if (c.size() == 5) { lock.notify(); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }, "t1").start(); } }
-
版本四
import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /* * 一道面试题:实现一个容器,提供两个方法,add size * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数 * 当个数到5个时,线程2给出提示并结束线程2 * * 相比较上一个例子,这里T1里面用wait释放锁,T2能够及时结束 * */ public class Container4 { volatile List<Object> lists = new ArrayList<>(); public void add(Object o) { lists.add(o); } public int size() { return lists.size(); } public static void main(String[] args) { Container4 c = new Container4(); Object lock = new Object(); new Thread(() -> { synchronized (lock) { System.out.println("t2启动"); if (c.size() != 5) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("t2结束"); lock.notify(); } }, "t2").start(); new Thread(()->{ System.out.println("t1启动"); synchronized (lock) { for (int i = 0; i < 10; i++) { c.add(new Object()); System.out.println("add " + i); if (c.size() == 5) { lock.notify(); try { lock.wait();//要释放锁,T2才能得到锁得以执行 } catch (InterruptedException e) { e.printStackTrace(); } } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("t1结束"); } },"t1").start(); } }
-
版本五:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /* * 一道面试题:实现一个容器,提供两个方法,add size * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数 * 当个数到5个时,线程2给出提示并结束线程2 * * CountDownLatch * 使用await和countdown方法替代wait和notify * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行 * 相当于是法令枪,运动员线程调用await等待,计数到0开始运行 * 当不涉及同步,只是涉及线程通信的时候,用synchronized加wait,notify就显得太重了 * */ public class Container5 { volatile List<Object> lists = new ArrayList<>(); public void add(Object o) { lists.add(o); } public int size() { return lists.size(); } public static void main(String[] args) { Container5 c = new Container5(); CountDownLatch latch = new CountDownLatch(1); new Thread(() -> { System.out.println("t2启动"); if (c.size() != 5) { try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("t2结束"); } }, "t2").start(); new Thread(() -> { System.out.println("t1启动"); for (int i = 0; i < 10; i++) { c.add(new Object()); System.out.println("add " + i); if (c.size() == 5) { latch.countDown(); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }, "t1").start(); } }
-
-
面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法, 能够支持两个生产者线程以及10个消费者线程的阻塞调用wait notifyAll
-
版本一:
import java.util.LinkedList; import java.util.concurrent.TimeUnit; /** * 面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法, * 能够支持两个生产者线程以及10个消费者线程的阻塞调用 * wait notifyAll * * 这里探究为什么大多数情况下wait和while是一起使用的, * 因为这里是有两个生产者线程并且容器容量是固定的,生产方法加了锁。 * 如果容器满了,这时候第一个生产者线程拿到了锁,他会判断有没有满,如果满了就等待,在等待 * 的时候会释放掉锁资源,这时候第二个生产者线程就会拿到锁,然后他也会判断是否满,因为容器是 * 满了,第二个生产者线程也会等待并且释放锁,当消费者消费之后唤醒所有线程,这时候两个生产者线程 * 都醒来了,因为要竞争锁资源,比如第一个生产者线程拿到了锁,他给容器又加到十了,陷入等待状态, * 锁资源释放掉,第二个生产者线程这时候拿到锁资源,他会继续执行(从上次睡眠的地方继续),如果是if * 的话,他在wait阻塞之前就已经执行了一次if,所以不会再执行,而是继续往下执行,那这时候就超过了 * 容器的容量。所以为了让他再一次判断,这里使用while */ public class Container1<T> { private final LinkedList<T> lists = new LinkedList<>(); private final int MAX = 10; private int count = 0; public synchronized void put(T t) { while (lists.size() == MAX) {// try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } lists.add(t); ++count; this.notifyAll(); } public synchronized T get() { T t = null; while (lists.size() == 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } t = lists.removeFirst(); count--; this.notifyAll(); return t; } public static void main(String[] args) { Container1<String> c = new Container1<>(); for (int i = 0; i < 100; i++) { new Thread(() -> { for (int j = 0; j < 5; j++) { System.out.println(c.get()); } }, "c" + i).start(); } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 2; i++) { new Thread(() -> { for (int j = 0; j < 25; j++) { c.put(Thread.currentThread().getName() + " " + j); } }, "p" + i).start(); } } }
-
版本二:
import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * 使用Lock和Condition来实现 * condition就是在什么条件下怎么做 * 对比上一个例子,Condition的范式可以更加精确的指定那些线程被唤醒 * */ public class Container2<T> { private final LinkedList<T> lists = new LinkedList<>(); private final int MAX = 10; private int count = 0; private Lock lock = new ReentrantLock(); private Condition producer = lock.newCondition(); private Condition consumer = lock.newCondition(); public void put(T t) { try { lock.lock(); while (lists.size() == MAX) { try { producer.await(); } catch (InterruptedException e) { e.printStackTrace(); } } lists.add(t); ++count; consumer.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public T get() { T t = null; try { lock.lock(); while (lists.size() == 0) { try { consumer.await(); } catch (InterruptedException e) { e.printStackTrace(); } } t = lists.removeFirst(); count--; producer.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } return t; } public static void main(String[] args) { Container2<String> c = new Container2<>(); for (int i = 0; i < 100; i++) { new Thread(() -> { for (int j = 0; j < 5; j++) { System.out.println(c.get()); } }, "c" + i).start(); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 2; i++) { new Thread(()->{ for (int j = 0; j < 25; j++) { c.put(Thread.currentThread().getName() + " " + j); } }, "p" + i).start(); } } }
-
-
ReentrantLock类的使用
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * ReentrantLock用于替代synchronized * 使用ReentrantLock可以完成同样的功能 * ReentrantLock必须要手动释放锁 * 使用sync锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁, * 因此经常在finally中进行锁的释放 * */ public class RLDemo1 { Lock lock = new ReentrantLock(); public void test1() { try { lock.lock();//this for (int i = 0; i < 3; i++) { System.out.println(i); TimeUnit.SECONDS.sleep(1); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void test2() { lock.lock(); System.out.println("test2 ..."); lock.unlock(); } public static void main(String[] args) { RLDemo1 rlDemo1 = new RLDemo1(); new Thread(rlDemo1::test1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rlDemo1::test2).start(); } }
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class RLDemo2 { Lock lock = new ReentrantLock(); public void test1() { try { lock.lock(); for (int i = 0; i < 2; i++) { System.out.println(i); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } /* * 使用ReentrantLock可以进行"尝试锁定tryLock",这样无法锁定,或者在指定时间内无法锁定, * 线程可以决定是否继续等待 * 可以使用tryLock进行尝试锁定,不管锁定是否,方法都将继续执行。 * 可以根据tryLock的返回值来判读是否锁定 * 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unlock的处理,必须放在finally中 * */ public void test2() { boolean locked = false; try { locked = lock.tryLock(3, TimeUnit.SECONDS); System.out.println("test2..." + locked); } catch (InterruptedException e) { e.printStackTrace(); } finally { if (locked){ System.out.println("test2 end"); lock.unlock(); } } } public static void main(String[] args) { RLDemo2 rlDemo2 = new RLDemo2(); new Thread(rlDemo2::test1).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(rlDemo2::test2).start(); } }
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; public class RLDemo3 { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Thread t1 = new Thread(() -> { try { lock.lock(); System.out.println("t1 start"); TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); System.out.println("t1 end"); } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { lock.unlock(); } }); //t1.start(); Thread t2 = new Thread(() -> { boolean locked = false; try { //lock.lock(); //使用lockInterruptibly来锁定可以对Interrupt方法做出响应 lock.lockInterruptibly(); System.out.println("t2 start"); TimeUnit.SECONDS.sleep(5); System.out.println("t2 end"); locked = true; } catch (InterruptedException e) { System.out.println("interrupted!"); } finally { if (locked) { lock.unlock(); } } }); t2.start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } t2.interrupt(); } }
import java.util.concurrent.locks.ReentrantLock; public class RLDemo4 extends Thread{ //ReentrantLock可以指定是否为公平锁,true为公平,默认为false private static ReentrantLock lock = new ReentrantLock(true); @Override public void run() { for (int i = 0; i < 100 ; i++) { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "获得锁"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } public static void main(String[] args) { RLDemo4 rlDemo4 = new RLDemo4(); Thread t1 = new Thread(rlDemo4); Thread t2 = new Thread(rlDemo4); t1.start(); t2.start(); } }
具体的代码我会放在附件中。可以直接搜索。