synchronized
synchronized关键字锁定的是对象不是代码块,demo中锁的是object对象的实例
锁定的对象有两种:1.类的实例 2.类对象
加synchronized关键字之后不一定能实现线程安全,具体还要看锁定的对象是否唯一。
看个demo:
private int count = 10; private Object object = new Object(); public void test(){ synchronized (object){ count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } }
synchronized(this)锁定的是当前类的实例,这里锁定的是Demo2类的实例
public class Demo2 {
private int count = 10;
public void test(){
//
synchronized (this){
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
}
}
直接加在方法声明上,相当于是synchronized(this)
public class Demo3 { private int count = 10; //直接加在方法声明上,相当于是synchronized(this) public synchronized void test(){ count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } }
synchronize关键字修饰静态方法锁定的是类的.class对象
静态方法中synchronize锁定代码块,锁定的对象不能是类的实例,只能是类的.class对象
public class Demo4 { private static int count = 10; //synchronize关键字修饰静态方法锁定的是类的.class对象 //静态方法中synchronize锁定代码块,锁定的对象不能是类的实例,只能是类的.class对象 public synchronized static void test(){ count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } public static void test2(){ synchronized (Demo4.class){//这里不能替换成this count--; } } }
锁定某对象o,如果o的属性发生改变,不影响锁的使用,但是如果o变成另外一个对象,则锁定的对象发生改变,应该避免将锁定对象的引用变成另外一个对象
t2能否执行?
public class Demo1 { Object o = new Object(); public void test(){ synchronized (o) { 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();//启动t1 try { TimeUnit.SECONDS.sleep(3);//睡眠3秒 } catch (InterruptedException e) { e.printStackTrace(); } Thread t2 = new Thread(demo :: test, "t2");//启动t2 demo.o = new Object(); //t2能否执行? t2.start(); } }
看下结果
不要以字符串常量作为锁定的对象,在下面,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 demo = new Demo2(); new Thread(demo :: test1,"test1").start(); //5秒后执行 new Thread(demo :: test2,"test2").start(); } }
同步代码快中的语句越少越好,比较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(); } } }
看下结果
相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自的锁标记,可以同时执行。
public class Demo2 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) { for (int i = 0; i < 5; i++) { //相比较Demo1,这里是new了五个对象,每个线程对应都拿到各自的锁标记,可以同时执行。 Demo2 demo = new Demo2(); new Thread(demo,"THREAD" + i).start(); } } }
同步方法和非同步方法是否可以同时调用?可以
public class Demo{ public synchronized void test1(){ System.out.println(Thread.currentThread().getName() + " test1 start..."); try { Thread.sleep(10000); } 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(); } }
脏读问题:实际业务当中应该看是否允许脏读,不允许的情况下对读方法也要加锁
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("huaan",100.0)).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(demo.getBalance("huaan"));// try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(demo.getBalance("huaan")); } }
把注释代码放开,看下结果
一个同步方法调用另外一个同步方法,能否得到锁?可以,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(); } }
重入锁的另外一种情况,继承
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........"); } }
碰到异常的情况,如果处理,不会自动释放锁,所以T2不会执行。
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不会执行。 try { int i = 1/0; }catch (Exception e){ System.out.println("出错了"); } } } } public static void main(String[] args) { Demo demo11 = new Demo(); Runnable r = () -> demo11.test(); new Thread(r, "t1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(r, "t2").start(); } }
碰到异常的情况,如果没有处理,会自动释放锁,所以T2可以执行。
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 demo11 = new Demo(); Runnable r = () -> demo11.test(); new Thread(r, "t1").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(r, "t2").start(); } }
看下结果
volatile
volatile 关键字,使一个变量在多个线程间可见,使用volatile关键字,会让所有线程都会读到变量的修改值
在下面的代码中,running是存在于堆内存的t对象中,当线程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();//指令重排序?内存一致性?cpu空转? try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } demo.running = false; } }
看下结果
加上volatile再看下结果
volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized或者说volatile保证不了原子性
比如说第一个线程加到100了,还没往上加,另外一个线程来了,把100拿过来执行方法,
然后第一个线程继续加到101,第二个线程也加到101,他两往回写都是101,线程不会管你加到哪儿了,虽然说加了2但是实际上只加了1.
public class Demo { volatile int count = 0; public 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(); //永远加不到10万 for (int i = 0; i < 10; i++) { threads.add(new Thread(demo::test, "thread-" + i)); } threads.forEach((o)->o.start()); threads.forEach((o)->{ try { o.join(); } catch (Exception e) { e.printStackTrace(); } }); System.out.println(demo.count); } }
永远加不到10万
相比较上一个例子,synchronized既保证了原子性又保证了可见性
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<Thread>(); for (int i = 0; i < 10; i++) { threads.add(new Thread(demo::test, "thread-" + i)); } threads.forEach((o)->o.start()); threads.forEach((o)->{ try { o.join(); } catch (Exception e) { e.printStackTrace(); } }); System.out.println(demo.count); } }
AtomicInteger
一道面试题:多个atomic类连续调用能否构成原子性?
public class Demo { AtomicInteger count = new AtomicInteger(0); //比如count加到999了,这时候一个线程拿到count判断,虽然.get方法保证原子性,但是他阻止 //不了其它线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了 public void test(){ for (int i = 0; i < 10000; i++) { //count.incrementAndGet();//相当于count++ if(count.get() < 1000){ count.incrementAndGet();//相当于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((o)->o.start()); threads.forEach((o)->{ try { o.join(); } catch (Exception e) { e.printStackTrace(); } }); System.out.println(demo.count); } }
两个原子操作一起用的时候无法保证原子性
看下另一种情况
public class Demo { AtomicInteger count = new AtomicInteger(0); //比如count加到999了,这时候一个线程拿到count判断,虽然.get方法保证原子性,但是他阻止 //不了其它线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了 public void test(){ for (int i = 0; i < 10000; i++) { count.incrementAndGet();//相当于count++ /*if(count.get() < 1000){ count.incrementAndGet();//相当于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((o)->o.start()); threads.forEach((o)->{ try { o.join(); } catch (Exception e) { e.printStackTrace(); } }); System.out.println(demo.count); } }
看结果
一道面试题:实现一个容器,提供两个方法,add,size写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束线程2
public class Container1 { List 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 (Exception e) { e.printStackTrace(); } } }," t1").start(); new Thread(()->{ while (true) { if (c.size() == 5) { break; } } System.out.println("t2线程结束"); }, "t2").start(); } }
这里list在两个线程之间不保证可见性,所以线程2始终结束不了
list前面加上volatile
volatile List lists = new ArrayList();
看下结果
wait()和notify()
public class Container3 { volatile List 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();//wait会释放锁 } catch (Exception 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();//notify不会释放锁,要等t1结束后才执行 } try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } } } }, "t1").start(); } }
wait会释放锁,notify不会释放锁,要等t1结束后才执行
相比较上一个例子,这里T1里面用wait释放锁,T2能够及时结束
public class Container4 { volatile List 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 (Exception 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 (Exception e) { e.printStackTrace(); } } try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } } } }, "t1").start(); } }
但是代码看上去很傻,我们优化一下
CountDownLatch
使用await和countdown方法替代wait和notify
CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
相当于是发令枪,运动员线程调用await等待,计数到0开始运行
当不涉及同步,只是涉及线程通信的时候,用synchronized加wait,notify就显得太重了
public class Container5 { volatile List 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 (Exception 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 (Exception e) { e.printStackTrace(); } } }, "t1").start(); } }
面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法,能够支持两个生产者线程以及10个消费者线程的阻塞调用
这里我们使用wait notifyAll来实现
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(); } } }
这里探究为什么大多数情况下wait和while是一起使用的,
* 因为这里是有两个生产者线程并且容器容量是固定的,生产方法加了锁。
* 如果容器满了,这时候第一个生产者线程拿到了锁,他会判断有没有满,如果满了就等待,在等待
* 的时候会释放掉锁资源,这时候第二个生产者线程就会拿到锁,然后他也会判断是否满,因为容器是
* 满了,第二个生产者线程也会等待并且释放锁,当消费者消费之后唤醒所有线程,这时候两个生产者线程
* 都醒来了,因为要竞争锁资源,比如第一个生产者线程拿到了锁,他给容器又加到十了,陷入等待状态,
* 锁资源释放掉,第二个生产者线程这时候拿到锁资源,他会继续执行(从上次睡眠的地方继续),如果是if
* 的话,他在wait阻塞之前就已经执行了一次if,所以不会再执行,而是继续往下执行,那这时候就超过了
* 容器的容量。所以为了让他再一次判断,这里使用while
我们优化一下:使用使用Lock和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) { producer.await();//await()和signalAll()一起用 } 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) { consumer.await(); } 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需要手动上锁和释放锁,
condition就是在什么条件下怎么做
对比上一个例子,Condition的方式可以更加精确的指定哪些线程被唤醒
ReentrantLock
reentrantlock用于替代synchronized,使用reentrantlock可以完成同样的功能
* reentrantlock必须要手动释放锁
* 使用synchronized锁定的话如果遇到异常,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("test 2..."); 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(); } }
使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,
* 线程可以决定是否继续等待
* 可以使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
* 可以根据tryLock的返回值来判定是否锁定
* 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中
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)抛出异常,所以要注意unclock的处理,必须放到finally中 */ public void test2(){ boolean locked = false; try { //locked = lock.tryLock(); 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(); } }
使用lockInterruptibly来锁定可以对Interrupt方法作出响应,进入异常处理
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(); } }
ReentrantLock可以指定是否为公平锁,true为公平,默认为false
public class RLDemo4 extends Thread{ //ReentrantLock可以指定是否为公平锁,true为公平,默认为false private static ReentrantLock lock = new ReentrantLock(false); @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(); } }
将其改为true,再看下结果