0%

JUC

JUC

多线程操作,先康康美团的博客吧!https://tech.meituan.com/2018/11/15/java-lock.html

Java多线程操作某个未加锁的资源

image-20210415202602405

使用synchronized之后:

1
2
3
4
5
6
7
8
9
10
11
class Ticket{
private int num = 150;

Lock lock = new ReentrantLock();

public synchronized void sale(){
if(num>0){
System.out.println(Thread.currentThread().getName()+"第"+(150-(--num))+"张票,剩余"+num);
}
}
}

image-20210415202842423

Lock锁

image-20210415203440781

image-20210415203511964

image-20210415205345896

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// lock三步
// 1、Lock lock = new ReentrantLock():
// 2、locl.lock();
// 3、finall处解锁:lock.unLock();
class Ticket2{
private int num = 50;

Lock lock = new ReentrantLock();

public synchronized void sale(){

//加锁
lock.lock();
try {
//业务代码
if(num>0){
System.out.println(Thread.currentThread().getName()+"第"+(150-(--num))+"张票,剩余"+num);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}

synchronized与lock两者的区别

​ 1、synchronized是一个内置的Java关键字,Lock是一个Java类;

​ 2、synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁;

​ 3、synchronized会自动释放锁,lock必须要手动释放锁,如果不释放锁会造成死锁

​ 4、synchronized使用时需要等待资源释放锁,而lock就不一定会等待下去;

​ 5、synchronized可重入锁,不可以中断,非公平;lock,可重入锁,可中断锁,可以设置为公平锁;

​ 6、适用范围:synchronized适合锁少量的代码同步问题,lock适合大量的同步代码。


锁是什么?如何判断锁住了什么资源?

生产者和消费者

​ 等待唤醒、通知

虚假唤醒

image-20210417114400161

常规版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//使用synchronized
//等待、业务、通知
class Data{ //资源类
private int num = 0;
public synchronized void increment() throws InterruptedException {
while(num!=0){ //必须使用while判断。如果使用if判断,当线程较多时会出现“虚假唤醒”,见上图
//等待
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
//通知其他线程我加一完了
this.notifyAll();
}

public synchronized void decrement() throws InterruptedException {
while(num==0){ //必须使用while判断。如果使用if判断,当线程较多时会出现“虚假唤醒”
this.wait();
//等待
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
this.notifyAll();
//通知其他线程我完成减一了
}
}


//主函数中调用
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"B").start();

JUC版本的生产者和消费者

image-20210417155358884

Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。

image-20210417155327193

一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例需要使用newCondition()方法。

例如,假设我们有一个有限的缓冲区,它支持puttake方法。 如果在一个空的缓冲区尝试一个take ,则线程将阻塞直到一个项目可用; 如果put试图在一个完整的缓冲区,那么线程将阻塞,直到空间变得可用。 我们希望在单独的等待集中等待put线程和take线程,以便我们可以在缓冲区中的项目或空间可用的时候使用仅通知单个线程的优化。 这可以使用两个Condition实例来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();

final Object[] items = new Object[100];
int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {
lock.lock(); try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally { lock.unlock(); }
}

public Object take() throws InterruptedException {
lock.lock(); try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}

ArrayBlockingQueue类提供此功能,因此没有理由实现此示例使用类。)

Condition实现可以提供Object监视器方法的行为和语义,例如有保证的通知顺序,或者在执行通知时不需要锁定。 如果一个实现提供了这样的专门的语义,那么实现必须记录这些语义。

需要注意的是Condition实例只是普通的对象,其本身作为一个目标synchronized语句,可以有自己的监视器waitnotification个方法调用。 获取Condition实例的监视器锁或使用其监视方法与获取与该Condition相关联的Condition或使用其waitingsignalling方法没有特定关系。 建议为避免混淆,您永远不会以这种方式使用Condition实例,除了可能在自己的实现之内。

除非另有说明,传递任何参数的null值将导致NullPointerException被抛出。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//使用lock
class Data2{
private int num = 0;

final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();

public void increment() throws InterruptedException {
try {
lock.lock();
while (num!=0){
notFull.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}

public void decrement() throws InterruptedException {
try {
lock.lock();
while(num==0){
//等待
notFull.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
notFull.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

Condition的优势:精准的通知和唤醒线程,即可以让线程有序执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//利用多个Condition实例来精确唤醒线程
class Data2{
private int num = 1;

final Lock lock = new ReentrantLock();

//利用同步监视器去精确地唤醒线程
final Condition conditionA = lock.newCondition();
final Condition conditionB = lock.newCondition();
final Condition conditionC = lock.newCondition();

public void A() throws InterruptedException {
try {
lock.lock();
while (num!=1){
conditionA.await();
}
num = 2;
System.out.println(Thread.currentThread().getName()+"->"+num);
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}

public void B() throws InterruptedException {
try {
lock.lock();
while(num!=2){
//等待
conditionB.await();
}
num=3;
System.out.println(Thread.currentThread().getName()+"->"+num);
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void C() throws InterruptedException {
try {
lock.lock();
while(num!=3){
//等待
conditionC.await();
}
num=1;
System.out.println(Thread.currentThread().getName()+"->"+num);
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}

1
2
3
4
5
6
7
8
9
//锁住class唯一一个模板
public static synchronized void sendSMS(){
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("SMS");
}

并发操作异常ConcurrentModificationException

1
2
3
4
//所有new出来的对象
public static synchronized void call(){
System.out.println("Call");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//ConcurrentModificationException
public class ListTest {
public static void main(String[] args){
// List<String> list = Collections.synchronizedList(new ArrayList<>());

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
list.add(UUID.randomUUID().toString().substring(0,7));
System.out.println(list);
}
},String.valueOf(i)).start();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SetTest {
public static void main(String[] args){
// set的本质就是map, key是无法重复的
// HashSet<String> set = new HashSet<>();

// Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 20; i++) {
new Thread(new Runnable() {
@Override
public void run() {
set.add(UUID.randomUUID().toString().substring(0,7));
System.out.println(set);
}
},"A").start();
}
}
}