synchronized 与 ReentrantLock
前言
1)synchronized函数锁的是this,ReentrantLock锁的是也是this,synchronized块自定义锁对象。
2)并发的控制在源头,将并发操作的核心即操作独占资源放在资源对象里,并加锁,把源头控制住,再多线程都不怕。
一、ArrayList为例
package com.xhu.java.thread;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
//测试lock锁到底在锁什么?
public class TestReentrantLock {
public static void main(String[] args) throws InterruptedException {
//一份核心资源
CoreResource cr = new CoreResource();
//两个线程用
Thread1 t1 = new Thread1(cr);
Thread2 t2 = new Thread2(cr);
// 启动两个线程同时操作资源
new Thread(t1).start();
new Thread(t2).start();
//等两个线程跑完
Thread.sleep(5000);
//查看核心资源的大小
System.out.println(cr.getResource().size());
//主线程再等待5秒
Thread.sleep(5000);
//如果两者的结果一致,说明其它线程已经跑完。该结果才可作为最后的结果
System.out.println(cr.getResource().size());
//test1:无锁情况操作同一资源,size本应该是200w,测试结果=1749696
//test2:加ReentrantLock在两个线程体,size本应该是200w,测试结果=15983189,初步猜测ReentrantLock锁的是this
//test3:加ReentrantLock在并发的源头,也就是独占资源处,本应该的size = 测试结果
//test4:synchronized 控制源头,也就是独占资源处,本应该的size = 测试结果
//test5:在两个线程体里用synchronized去锁独占资源,本应该的size = 测试结果
//总结:1.synchronized函数与ReentrantLock锁的都是this。2.并发源于独占资源处,所以并发操作应该写在源头处,管理好源头,其它线程就不会乱。
}
}
//争夺资源的线程1
class Thread1 implements Runnable {
//争夺的核心资源
private CoreResource cr;
private ReentrantLock rl = new ReentrantLock();
public Thread1(CoreResource cr) {
this.cr = cr;
}
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
synchronized (cr) {
cr.add(i);
}
}
}
}
//争夺资源的线程2
class Thread2 implements Runnable {
//核心资源只有一份
private CoreResource cr;
//private ReentrantLock rl = new ReentrantLock();
public Thread2(CoreResource cr) {
this.cr = cr;
}
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
synchronized (cr) {
cr.add(i);
}
}
}
}
//独占资源类ArrayList
class CoreResource {
private List<Integer> resource = new ArrayList<>();
private ReentrantLock rl = new ReentrantLock();
//独占资源应该在独占资源处操作,便于管理,管理并发的源头。
public void add(int i) {
//rl.lock();
resource.add(i);
//rl.unlock();
}
public List<Integer> getResource() {
return resource;
}
public void setResource(List<Integer> resource) {
this.resource = resource;
}
}
总结
1)分清楚一个线程体多个线程执行、多个线程体多个线程执行,但是它们都操作同一资源的区别。
2)独占资源才是并发的核心,设计并管理好独占资源类。
附录
[1] Java 多线程入门