这里只是提一嘴,AQS这是高并发最核心的内容。以下所有的东西,都是通过AQS来实现的,也就是说内部是有(线程等待)队列的。
新出现的锁,锁竞争用的都是CAS操作。阻塞都是用的park/unpark。
ReentrantLock可重入锁,synchronized 本身就是一种。什么叫可重入,就是我锁了一下之后呢可以对这把锁再锁一下。synchronized必须设计成可重入的,不然子类实现调用父类父类是没法实现的。
/**
* reentrantlock用于替代synchronized
* 本例中由于m1锁定this,只有m1执行完毕的时候,m2才能执行
* 这里是复习synchronized最原始的语义
* @author mashibing
*/
package com.mashibing.juc.c_020;
import java.util.concurrent.TimeUnit;
public class T01_ReentrantLock1 {
synchronized void m1() {
for(int i=0; i<10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
if(i == 2) m2();
}
}
synchronized void m2() {
System.out.println("m2 ...");
}
public static void main(String[] args) {
T01_ReentrantLock1 rl = new T01_ReentrantLock1();
new Thread(rl::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//new Thread(rl::m2).start();
}
}
结论:
用synchronized的地方完全可以用ReentrantLock来替换。使用reentrantlock可以完成同样的功能。
/**
* reentrantlock用于替代synchronized
* 由于m1锁定this,只有m1执行完毕的时候,m2才能执行
* 这里是复习synchronized最原始的语义
*
* 使用reentrantlock可以完成同样的功能
* 需要注意的是,必须要必须要必须要手动释放锁(重要的事情说三遍)
* 使用syn锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放
* @author mashibing
*/
package com.mashibing.juc.c_020;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T02_ReentrantLock2 {
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock(); //synchronized(this)
for (int i = 0; i < 10; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
void m2() {
try {
lock.lock();
System.out.println("m2 ...");
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
T02_ReentrantLock2 rl = new T02_ReentrantLock2();
new Thread(rl::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(rl::m2).start();
}
}
可以用tryLock尝试锁定,sync如果上来之后搞不定那把锁的话,就会阻塞了。但如果用ReentrantLock,可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待
/**
* 使用reentrantlock
* @author mashibing
*/
package com.mashibing.juc.c_020;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T03_ReentrantLock3 {
Lock lock = new ReentrantLock();
void m1() {
try {
lock.lock();
for (int i = 0; i < 3; i++) {
TimeUnit.SECONDS.sleep(1);
System.out.println(i);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/**
* 使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
* 可以根据tryLock的返回值来判定是否锁定
* 也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中
*/
void m2() {
boolean locked = false;
try {
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2 ..." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(locked) lock.unlock();
}
}
public static void main(String[] args) {
T03_ReentrantLock3 rl = new T03_ReentrantLock3();
new Thread(rl::m1).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(rl::m2).start();
}
}
sync在wait()以后,必须得等到别人notify()之后才能醒来,不然的话自己是醒不过来的。而以lock.lockInterruptibly()方式加锁,可以对线程interrupt方法做出响应,在一个线程等待锁的过程中,可以被打断
/**
* 使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应,
* 在一个线程等待锁的过程中,可以被打断
*
* @author mashibing
*/
package com.mashibing.juc.c_020;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public class T04_ReentrantLock4 {
public static void main(String[] args) {
Lock 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(()->{
try {
//lock.lock();
lock.lockInterruptibly(); //可以对interrupt()方法做出响应
System.out.println("t2 start");
TimeUnit.SECONDS.sleep(5);
System.out.println("t2 end");
} catch (InterruptedException e) {
System.out.println("interrupted!");
} finally {
lock.unlock();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt(); //打断线程2的等待
}
}
公平锁的意思是,谁等在前面先让谁执行,而不是说让后来的先执行。当我们 new 一个 ReentrantLock,传一个参数为true,表示创建一个公平锁。如果是公平锁,一个线程上来后,先回检查等待队列里头有没有线程,如果有的话就进入等待队列等着,等别人先运行
package com.mashibing.juc.c_020;
import java.util.concurrent.locks.ReentrantLock;
public class T05_ReentrantLock5 extends Thread {
private static ReentrantLock lock=new ReentrantLock(true); //参数为true表示为公平锁,请对比输出结果
public void run() {
for(int i=0; i<100; i++) {
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"获得锁");
}finally{
lock.unlock();
}
}
}
public static void main(String[] args) {
T05_ReentrantLock5 rl=new T05_ReentrantLock5();
Thread th1=new Thread(rl);
Thread th2=new Thread(rl);
th1.start();
th2.start();
}
}
查看另一篇文章
ReentrantLock是CAS,现在除了sync多数内部用的都是CAS。但是啊,这么聊的话就要聊的很深了。实际上AQS的内部实际上是park()和unpark(),也不是在全部的情况下都是CAS,它还是有一个锁升级的概念,只不过做的比较隐蔽,你在等待队列的时候,如果拿不到,还是会进入阻塞状态,只不过它前面呢会有一个CAS的状态,它不像原来呢,就直接就进入阻塞态了。所以它内有一个阻塞的概念,它用的是什么呢,是LockSupport.park() 和 LockSupport.unpark()。这个后续再深入。
count down 倒数,latch 门栓,CountDownLatch 就是“倒数5、4、3、2、1然后门就开了”,这么一个意思。
package com.mashibing.juc.c_020;
import java.util.concurrent.CountDownLatch;
public class T06_TestCountDownLatch {
public static void main(String[] args) {
usingJoin();
usingCountDownLatch();
}
private static void usingCountDownLatch() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for(int i=0; i<threads.length; i++) {
threads[i] = new Thread(()->{
int result = 0;
for(int j=0; j<10000; j++) result += j;
latch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end latch");
}
private static void usingJoin() {
Thread[] threads = new Thread[100];
for(int i=0; i<threads.length; i++) {
threads[i] = new Thread(()->{
int result = 0;
for(int j=0; j<10000; j++) result += j;
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end join");
}
}
循环栅栏,这个就像是发令枪一样,什么时候人满了,就把栅栏推倒,然后呢人就出去了。然后栅栏重新起来,然后新来的人满啦,再推倒。就这样循环。
package com.mashibing.juc.c_020;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class T07_TestCyclicBarrier {
public static void main(String[] args) {
//CyclicBarrier barrier = new CyclicBarrier(20);
CyclicBarrier barrier = new CyclicBarrier(20, () -> System.out.println("人满,发车"));
/*CyclicBarrier barrier = new CyclicBarrier(20, new Runnable() {
@Override
public void run() {
System.out.println("人满,发车");
}
});*/
for(int i=0; i<100; i++) {
new Thread(()->{
try {
// 相当于计数
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
package com.mashibing.juc.c_020;
import java.util.Random;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
public class T09_TestPhaser2 {
static Random r = new Random();
static MarriagePhaser phaser = new MarriagePhaser();
static void milliSleep(int milli) {
try {
TimeUnit.MILLISECONDS.sleep(milli);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
phaser.bulkRegister(7);
for(int i=0; i<5; i++) {
new Thread(new Person("p" + i)).start();
}
new Thread(new Person("新郎")).start();
new Thread(new Person("新娘")).start();
}
static class MarriagePhaser extends Phaser {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase) {
case 0:
System.out.println("所有人到齐了!" + registeredParties);
System.out.println();
return false;
case 1:
System.out.println("所有人吃完了!" + registeredParties);
System.out.println();
return false;
case 2:
System.out.println("所有人离开了!" + registeredParties);
System.out.println();
return false;
case 3:
System.out.println("婚礼结束!新郎新娘抱抱!" + registeredParties);
return true;
default:
return true;
}
}
}
static class Person implements Runnable {
String name;
public Person(String name) {
this.name = name;
}
public void arrive() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 到达现场!\n", name);
phaser.arriveAndAwaitAdvance();
}
public void eat() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 吃完!\n", name);
phaser.arriveAndAwaitAdvance();
}
public void leave() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 离开!\n", name);
phaser.arriveAndAwaitAdvance();
}
private void hug() {
if(name.equals("新郎") || name.equals("新娘")) {
milliSleep(r.nextInt(1000));
System.out.printf("%s 洞房!\n", name);
phaser.arriveAndAwaitAdvance();
} else {
phaser.arriveAndDeregister();
//phaser.register()
}
}
@Override
public void run() {
//阶段1)大家到齐
arrive();
//阶段2)大家吃饭
eat();
//阶段3)大家离开
leave();
//阶段4)新郎新娘入洞房
hug();
}
}
}
读写锁的概念实际就是共享锁和排他锁。读锁=共享锁,写锁=排他锁。
举个例子:
当好多线程来访问它的时候,有的是读线程,有的是写线程,在不产生数据不一致的情况下。这个时候得给它枷锁,不管是读的时候还是写的时候。但是这种情况下效率非常非常的低,尤其是读线程很多的时候,所以做成这样的锁,在我读的时候加这样一把锁,这把锁呢可以给别人也来继续读,但是别人的写线程来申请我就不给他,我正在读呢。这样效率就比较高了。
package com.mashibing.juc.c_020;
import java.util.Random;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class T10_TestReadWriteLock {
//static Lock lock = new ReentrantLock(); // 互斥锁(排他锁)
private static int value;
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock readLock = readWriteLock.readLock();
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read over!");
//模拟读取操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int v) {
try {
lock.lock();
Thread.sleep(1000);
value = v;
System.out.println("write over!");
//模拟写操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
//Runnable readR = ()-> read(lock);
Runnable readR = ()-> read(readLock);
//Runnable writeR = ()->write(lock, new Random().nextInt());
Runnable writeR = ()->write(writeLock, new Random().nextInt());
//读锁是共享锁。当一个对象被加上一把读锁的时候,其他的线程也是可以继续读的,所以理论上这18个线程可以1秒钟结束。
for(int i=0; i<18; i++) new Thread(readR).start();
//写锁是排他的。当一个线程在写的时候,其他线程既不能读也不能写。
for(int i=0; i<2; i++) new Thread(writeR).start();
}
}
所在在“读多写少”的情况下,使用读写锁ReadWriteLock,比单纯的排他锁ReentrantLock效率要大大的提高。
以后一般不写这些新的,多数时候还用 synchronized,哈哈哈。只有特别特别追求效率的时候,以后无论什么,上来先用 synchronized 不要想其他的。这个东西呢,面试是第一位的。如果碰到说 synchronized 效率不够了,要求性能上要提高了,好,尝试用其他锁。现在好像用的都是分布式锁(Redis、Zookeeper、数据库(效率较低))。
叫信号灯。可以是公平锁和非公平锁。在线程内部,通过 s.acquire() 获得这把锁。
package com.mashibing.juc.c_020;
import java.util.concurrent.Semaphore;
public class T11_TestSemaphore {
public static void main(String[] args) {
//Semaphore s = new Semaphore(2);
// 第二个参数控制是否是公平锁
Semaphore s = new Semaphore(2, true);
//允许一个线程同时执行
//Semaphore s = new Semaphore(1);
new Thread(()->{
try {
// 这是一个阻塞操作。可以有好多个线程同时acquire(),但是能acquire()到的只有2个
// 获得这把锁。这个线程想要继续往下执行,就要获得这把锁。
s.acquire();
System.out.println("T1 running...");
Thread.sleep(200);
System.out.println("T1 running...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release();
}
}).start();
new Thread(()->{
try {
s.acquire();
System.out.println("T2 running...");
Thread.sleep(200);
System.out.println("T2 running...");
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
交换器,两线程之间交换数据用的。线程之间通信的方式非常多,这只是其中的一种。怎么用呢,看下面:
import java.util.concurrent.Exchanger;
public class T12_TestExchanger {
static Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(()->{
String s = "T1";
try {
s = exchanger.exchange(s); // 该方法是阻塞的。第一个线程执行到这个方法的时候,阻塞,等第二个线程也执行这个方法,然后交换数据
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "t1").start();
new Thread(()->{
String s = "T2";
try {
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);
}, "t2").start();
}
}