走进JUC的世界
概念
同步锁:synchronized、Lock区别
1、synchronized是不需要进行手动解锁
2、synchronized可以锁方法、锁同步代码块
3、synchronized是Java自带关键字
4、Lock锁是一个类且它拥有synchronized的所有功能还具备扩展
5、Lock锁的实现类ReentrantLock可以实现公平和非公平锁
6、Lock锁需要手动加锁和手动解锁
7、synchronized不可中断而Lock锁可以实现中断
synchronized
- 当修饰方法时:锁的是方法调用者(this)
- 当使用static synchronized修饰方法时,锁的是Class对象(类名.class)
- 也可以使用代码块方式来锁取Class对象(类名.class)
Lock : 主要使用到的实现类ReentrantLock(可重入锁)
- ReentrantLock() -> 非公平锁(默认)(所谓非公平锁既是可以进行插队操作)
- ReentrantLock(true) -> 公平锁(所谓公平锁就是需要排队,不可以进行插队操作)
集合的线程不安全情况和解决方案
List
: ArrayList不安全List,但是在单线程情况下是高效的!
多线程下错误案例:
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
final Integer temp = i;
new Thread(()->{
list.add(temp);
System.out.println(list);
}, String.valueOf(temp)).start();
}
//结果出现并发修改异常ConcurrentModificationException
[0, 1, 2, 3, 5, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 18, 20, 21, 23, 22, 24, 25, 26, 27, 29, 28]
//Exception in thread "11" Exception in thread "15" Exception in thread "19" java.util.ConcurrentModificationException
解决方案:
//1.使用集合安全类进行转换
List<Integer> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i < 30; i++) {
final Integer temp = i;
new Thread(()->{
list.add(temp);
System.out.println(list);
}, String.valueOf(temp)).start();
}
//2.使用List对应的Vector
List<Integer> list = new Vector<>();
for (int i = 0; i < 30; i++) {
final Integer temp = i;
new Thread(()->{
list.add(temp);
System.out.println(list);
}, String.valueOf(temp)).start();
}
//3.使用CopyOnWriteArrayList
List<Integer> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 30; i++) {
final Integer temp = i;
new Thread(()->{
list.add(temp);
System.out.println(list);
}, String.valueOf(temp)).start();
}
Set集合
:HashSet
多线程下错误案例
Set<Integer> set = new HashSet<>();
for (int i = 0; i < 30; i++) {
final Integer temp = i;
new Thread(()->{
set.add(temp);
System.out.println(set);
}, String.valueOf(temp)).start();
}
// 结果:抛出ConcurrentModificationException并发修改异常!
解决方案:
// 1.使用Collections.synchronizedSet安全集合包装
Set<Integer> set = Collections.synchronizedSet(new HashSet<>());
// 2.使用CopyOnWriteArraySet
Set<Integer> set = new CopyOnWriteArraySet<>();
map集合
:HashMap
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < 30; i++) {
final Integer temp = i;
new Thread(()->{
map.put(String.valueOf(temp), temp);
System.out.println(map);
}, String.valueOf(temp)).start();
}
//结果:Exception in thread "6" java.util.ConcurrentModificationException并发修改异常
解决方案:
// 1.ConcurrentHashMap
Map<String, Object> map = new ConcurrentHashMap<>();
for (int i = 0; i < 70; i++) {
final Integer temp = i;
new Thread(()->{
map.put(String.valueOf(temp), temp);
System.out.println(map);
}, String.valueOf(temp)).start();
}
// 2.Hashtable(效率低)
常用线程辅助类
CountDownLatch(减法计数器)
CountDownLatch countDownLatch = new CountDownLatch(10); // 传入一个数字,要执行多少次
countDownLatch.countDown(); // 每次执行完一个任务后,进行减1操作
countDownLatch.await(); // 等待计数器归零,只有等上面执行次数完毕后,才能执行后面的操作
CyclicBarrier(加法计数器)
CyclicBarrier cyclicBarrier = new CyclicBarrier(10); // 初始化计数器容量,默认构造Runnable为null
// 当计数器到达10的时候,就执行Runnable里面的具体操作
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Runnable() {
@Override
public void run() {
System.out.println("executor other thing!");
}
});
cyclicBarrier.await(); // 等待计数器到达初始化计数器值,然后才能执行下面操作!
栗子:
//初始化cyclicBarrier加法计数器
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Runnable() {
@Override
public void run() {
System.out.println("执行到第十个啦,完结!");
}
});
for (int i = 1; i <= 10; i++) {
int u = i;
new Thread(()->{
try {
System.out.println("执行到第" + u +"个了, 还剩" + (10 - u) + "个");
//每执行完一个线程就进行加一操作!当执行完第十个就触发cyclicBarrier初始化中的Runnable接口实现
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
Semaphore(信号量)
Semaphore
: 一般用于限流
情况
semaphore.acquire():获得线程使用权限
semaphore.release():释放线程使用权限
Semaphore semaphore = new Semaphore(2);
for (int i = 1; i <= 4; i++) {
new Thread(() -> {
try {
// 得到线程执行权限,当线程数到达了信号量初始化容量,其他线程就会等待(阻塞)当前线程执行完毕并释放执行权限才可继续执行!
semaphore.acquire();
System.out.println("当前线程:" + Thread.currentThread().getName() + "开始执行...");
TimeUnit.SECONDS.sleep(2);
System.out.println("当前线程:" + Thread.currentThread().getName() + "执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); //释放线程执行权限
}
}, String.valueOf(i)).start();
}
}
// 结果
当前线程:1开始执行...
当前线程:2开始执行...
当前线程:1执行完毕
当前线程:2执行完毕
// 到达信号量最大容量,其他线程就进行等待(阻塞)
当前线程:3开始执行...
当前线程:4开始执行...
当前线程:3执行完毕
当前线程:4执行完毕
读写锁
ReadWriteLock
主要使用到:ReentrantReadWriteLock(实现类)
概念
读写锁共存
- 读 -> 读 可以共存
- 读 -> 写 不能共存(不能边修改边读取,就会出现读取的数据不正确情况)
- 写 -> 写 不能共存(可能出现一个线程正在修改原来的值,另一个线程也在修改原来的值,出现两个线程修改后,最后读取的数据不是自己修改的数据)
独占/共享锁
- 独占锁:也就是写锁,同一时刻只能有一个线程可以对数据进行写的操作
- 共享锁:也就是读锁,同一时刻可以出现多个线程对数据进行读取的操作,且读取的数据都是同一份数据
//开启两个读写线程,分别进行写和读操作
for (int i = 0; i < 5; i++) {
final Integer temp = i;
new Thread(()->{
mapDemo.put(String.valueOf(temp), temp + 10000);
}, "线程->" + String.valueOf(temp)).start();
}
for (int i = 5; i < 10; i++) {
final Integer temp = i;
new Thread(()->{
mapDemo.get(String.valueOf(temp));
}, "线程->" + String.valueOf(temp)).start();
}
// 初始化读写锁
private volatile Map<String, Object> map = new ConcurrentHashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key, Object value) {
//写入加锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始写入.....");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕.....");
}finally {
//写完释放锁
readWriteLock.writeLock().unlock();
}
}
public Object get(String key) {
//读取加锁
readWriteLock.readLock().lock();
Object object = null;
try {
System.out.println(Thread.currentThread().getName() + "开始读取----------->");
object = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完成----------->");
}finally {
//读取解锁
readWriteLock.readLock().unlock();
}
return object;
}
// 运行结果:发现写入的时候,总是只有一个线程可以在同一时间进行写入,而读取可以多个线程同时读取
线程->1开始写入.....
线程->1写入完毕.....
线程->0开始写入.....
线程->0写入完毕.....
线程->3开始写入.....
线程->3写入完毕.....
线程->2开始写入.....
线程->2写入完毕.....
线程->4开始写入.....
线程->4写入完毕.....
线程->5开始读取----------->
线程->5读取完成----------->
线程->7开始读取----------->
线程->8开始读取----------->
线程->8读取完成----------->
线程->6开始读取----------->
线程->9开始读取----------->
线程->9读取完成----------->
线程->7读取完成----------->
线程->6读取完成----------->
阻塞队列
- ArrayBlockingQueue
- add()与offer()区别:add在超出容量时会抛出异常,而offer则不会抛出异常,而是拒绝添加到队列中!
- 移除区别(remove()与poll()区别):当队列中无元素时,remove会抛出异常,而poll则是返回null
- 查看队首(element()与peek()区别):当队列为空时,element会抛出异常,而peek
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(4);
arrayBlockingQueue.add("A");
arrayBlockingQueue.add("B");
arrayBlockingQueue.add("C");
arrayBlockingQueue.add("D");
// arrayBlockingQueue.add("E");
System.out.println(arrayBlockingQueue);
//结果:
[A, B, C, D]
/**
注意:
1. 当元素超过队列的容量时,就会抛出异常java.lang.IllegalStateException: Queue full
2. 当添加null时,抛出空指针异常 java.lang.NullPointerException
3.使用offer代替add使用
*/
//1.错误案例:容量为4,但是添加了五个元素
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(4);
arrayBlockingQueue.add("A");
arrayBlockingQueue.add("B");
arrayBlockingQueue.add("C");
arrayBlockingQueue.add("D");
arrayBlockingQueue.add("E");
System.out.println(arrayBlockingQueue);
//结果:
Exception in thread "main" java.lang.IllegalStateException: Queue full
//2.错误案例:添加null数据,抛出空指针异常
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(4);
arrayBlockingQueue.add("A");
arrayBlockingQueue.add("B");
arrayBlockingQueue.add("C");
arrayBlockingQueue.add(null);
System.out.println(arrayBlockingQueue);
//结果:
Exception in thread "main" java.lang.NullPointerException
//3.使用offer代替add添加元素
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(4);
arrayBlockingQueue.offer("A");
arrayBlockingQueue.offer("B");
arrayBlockingQueue.offer("C");
arrayBlockingQueue.offer("D");
arrayBlockingQueue.offer("E");
System.out.println(arrayBlockingQueue);
//add与offer区别:add在超出容量时会抛出异常,而offer则不会抛出异常,而是拒绝添加到队列中!
//结果:
[A, B, C, D]
ArrayBlockingQueue 延迟等待
//延迟添加等待----------------> offer()
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(4); System.out.println(arrayBlockingQueue.offer("A"));
System.out.println(arrayBlockingQueue.offer("B"));
System.out.println(arrayBlockingQueue.offer("C"));
System.out.println(arrayBlockingQueue.offer("D"));
//延迟12秒添加,如果队列已满就返回false(表示添加失败)
System.out.println(arrayBlockingQueue.offer("E", 12, TimeUnit.SECONDS)); System.out.println(arrayBlockingQueue);
//结果:
true
true
true
true
false
[A, B, C, D] // 延迟取出等待-------------> poll()
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(4); arrayBlockingQueue.offer("A");
arrayBlockingQueue.offer("B");
arrayBlockingQueue.offer("C");
arrayBlockingQueue.offer("D");
arrayBlockingQueue.offer("E", 2, TimeUnit.SECONDS); System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));
// 结果:
A
B
C
D
//延迟等待2秒钟再弹出,如果队列为空,就返回null
null
同步队列(SynchronousQueue)
- 特性:只能存储一个对象/值,当
存入之后必须等待取出之后
才能进行再次存入
- 特性:只能存储一个对象/值,当
栗子:
new Thread(()->{
try {
synchronousQueue.put(1);
System.out.println(Thread.currentThread().getName() + ":put " + 1);
synchronousQueue.put(2);
System.out.println(Thread.currentThread().getName() + ":put " + 2);
synchronousQueue.put(3);
System.out.println(Thread.currentThread().getName() + ":put " + 3);
} catch (InterruptedException e) {
e.printStackTrace();
} }
, "put线程:").start();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName() + ":take -> " + synchronousQueue.take());
System.out.println(Thread.currentThread().getName() + ":take -> " + synchronousQueue.take());
System.out.println(Thread.currentThread().getName() + ":take -> " + synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
} }
, "take线程:").start();
//结果:
put线程::put 1
take线程::take -> 1
put线程::put 2
take线程::take -> 2
put线程::put 3
take线程::take -> 3
线程池
线程复用(节约了系统资源)
控制最大并发数(当达到线程池容量,就需要等待其他线程完成,才能继续进入)
管理线程
Executors线程池
ExecutorService executorService = Executors.newSingleThreadExecutor(); // 单个线程的池子
ExecutorService executorService = Executors.newFixedThreadPool(10); //开启十个固定线程的池子
ExecutorService executorService = Executors.newCachedThreadPool(); //可伸缩线程池, 如果线程池中线程已全被使用就创建新的线程池
newSingleThreadExecutor
ExecutorService executorService = Executors.newSingleThreadExecutor(); // 单个线程的池子
try {
for (int i1 = 0; i1 < 10; i1++) {
//执行线程
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行了线程..");
}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
executorService.shutdown();
}
//结果:只有一个线程在重复利用执行
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-1 执行了线程..
newFixedThreadPool
ExecutorService executorService = Executors.newFixedThreadPool(5); //开启十个固定线程的池子
try {
for (int i = 0; i < 10; i++) {
//执行线程
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行了线程..");
}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
executorService.shutdown();
}
//结果:五个不同的线程重复使用
pool-1-thread-4 执行了线程..
pool-1-thread-1 执行了线程..
pool-1-thread-4 执行了线程..
pool-1-thread-3 执行了线程..
pool-1-thread-2 执行了线程..
pool-1-thread-5 执行了线程..
pool-1-thread-2 执行了线程..
pool-1-thread-3 执行了线程..
pool-1-thread-4 执行了线程..
pool-1-thread-1 执行了线程..
newCachedThreadPool
ExecutorService executorService = Executors.newCachedThreadPool(); //可伸缩线程池, 如果线程池中线程已全被使用就创建新的线程池
try {
for (int i = 0; i < 10; i++) {
//开启线程
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 执行了线程..");
}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
executorService.shutdown();
}
//结果:开启新线程,当已开启的线程执行完毕,放入池子中又可以进行使用,如果开启的线程都还在执行中,就创建新的线程
pool-1-thread-1 执行了线程..
pool-1-thread-6 执行了线程..
pool-1-thread-5 执行了线程..
pool-1-thread-3 执行了线程..
pool-1-thread-4 执行了线程..
pool-1-thread-2 执行了线程..
pool-1-thread-8 执行了线程..
pool-1-thread-7 执行了线程..
pool-1-thread-9 执行了线程..
pool-1-thread-10 执行了线程..
线程池参数
- 7大参数
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //线程存活时间
TimeUnit unit, //线程时间单元
BlockingQueue<Runnable> workQueue) { //阻塞队列
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory() //默认线程工厂
, defaultHandler); //线程拒绝策略(当达到了最大线程数时,采用线程拒绝策略)
}
Spring自带的任务执行器线程池
@Bean("scheduledTaskExecutor")
public ThreadPoolTaskExecutor scheduledTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //创建任务执行器线程池
executor.setCorePoolSize(3);//设置核心线程数
executor.setMaxPoolSize(5); //设置最大线程数
executor.setQueueCapacity(1024*100); //设置一个队列容量
executor.setThreadNamePrefix("parking-index-task"); //线程名称
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
executor.initialize(); //初始化线程池
return executor;
}
线程四大拒绝策略应用场景
AbortPolicy: 当队列中线程已满,就抛出异常
DiscardPolicy:当队列满了,就丢弃任务,不会抛出异常
CallerRunsPolicy: 队列已满时,就使用调用者的线程去执行,当处理器关闭就丢弃此线程需求
DiscardOldestPolicy:当队列满了,去尝试和较早的线程竞争,当最早的线程即将执行完成就把当前任务使用即将完成的线程执行
源码解释:
AbortPolicy:
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
解释:总是把RejectedExecutionException。Params: r—请求执行的可运行任务e—尝试执行该任务的执行器抛出:RejectedExecutionException—always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
DiscardPolicy:
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
解释:什么都不做,这有丢弃任务r的效果。参数:r -请求被执行的可运行任务e -试图执行该任务的执行程序
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
CallerRunsPolicy:
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
解释:在调用者的线程中执行任务r,除非执行器已经关闭,在这种情况下,任务将被丢弃。参数:r—请求执行的可运行任务e—尝试执行该任务的执行程序
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
DiscardOldestPolicy:
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
解释:获取并忽略执行器将执行的下一个任务(如果有一个任务立即可用),然后重试执行任务r,除非执行器被关闭,在这种情况下,任务r将被丢弃。参数:r—请求执行的可运行任务e—尝试执行该任务的执行程序
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
Cpu密集型
int availableProcessors = Runtime.getRuntime().availableProcessors(); //获取Cpu核数,适合设置核心线程池的大小
IO密集型
int availableProcessors = Runtime.getRuntime().availableProcessors(); //
int maximumPoolSize = availableProcessors * 2; // Io密集型一般设置为Cpu核数的两倍,防止
ForkJoin
- 任务拆分
public class DoMain extends RecursiveTask<Long> {
private Long start;
private Long end;
private final Long threshold = 10_0000_0000L;
public DoMain(Long start, Long end) {
this.start = start;
this.end = end;
}
/**
递归分解大数据,每次进行两段两段操作
*/
@Override
protected Long compute() {
Long res = 0L;
if ((end - start) > threshold) {
Long middle = (end + start) / 2;
//分两次进行计算
ForkJoinTask<Long> fork1 = new DoMain(start, middle).fork();
Long res1 = fork1.join();
ForkJoinTask<Long> fork2 = new DoMain(middle, end).fork();
Long res2 = fork2.join();
res = res1 + res2;
} else {
for (Long i = start; i < end; i++) {
res += i;
}
}
return res;
}
}
//这样创建线程不规范,这里只是简易操作!
new Thread(() -> {
long l = System.currentTimeMillis();
DoMain doMain = new DoMain(0L, 500_0000_0000L);
ForkJoinTask<Long> submit = new ForkJoinPool().submit(doMain);
try {
System.out.println("forkJoin输出结果:" + submit.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("forkJoin所用时间: " + (System.currentTimeMillis() - l));
}).start();
//这样创建线程不规范,这里只是简易操作!
new Thread(() -> {
long start = System.currentTimeMillis();
Long res = 0L;
for (Long i = 0L; i < 500_0000_0000L; i++) {
res += i;
}
System.out.println("普通循环输出结果:" + res);
System.out.println("普通所用时间" + (System.currentTimeMillis() - start));
}).start();
计算对比
stream环输出结果: 124999999750000000
stream所用时间2304
普通循环输出结果:124999999750000000
普通所用时间14116
forkJoin输出结果:124999999750000000
forkJoin所用时间: 14468
stream流计算
new Thread(() -> {
long start = System.currentTimeMillis();
long longStream = LongStream.range(0L, 5_0000_0000L).parallel().reduce(0L, Long::sum);
System.out.println("stream环输出结果: " + longStream);
System.out.println("stream所用时间" + (System.currentTimeMillis() - start));
}).start();
volatile
- 保证了可见性
- 不保证原子性(也就是多线程情况下,无法保证同一个值被多个线程修改)
- 保证了禁止指令重排(当程序启动时,它可能并不是按照我们代码的顺序执行,比如初始化,可能就不是按照我们写的代码步骤来的,这就是指令重排,保证指令不重排就可以使用volatile关键字进行声明)
/**
* 1.使用volatile禁止指令重排
* 2. 使用AtomicInteger原子类保证是原子操作
*/
public static volatile AtomicInteger num = new AtomicInteger();
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
//进行加一操作
num.getAndIncrement();
}
}).start();
}
//当线程数大于2时,暂停main线程,让给其他线程执行
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(num);
原子类操作源码
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//原子类底层代码,使用了CAS(比较替换算法,也就是自旋锁)
// compareAndSwapInt底层是调用c++操作内存,对应的是native关键字
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
CAS简单实现
栗子:
@SneakyThrows
public static void main(String[] args) {
CasLock casLock = new CasLock();
new Thread(()->{
try {
casLock.lock();
}catch (Exception e) {
e.printStackTrace();
}finally {
casLock.unLock();
}
}, "Thread1").start();
TimeUnit.SECONDS.sleep(2);
new Thread(()->{
try {
casLock.lock();
}catch (Exception e) {
e.printStackTrace();
}finally {
casLock.unLock();
}
}, "Thread2").start();
}
public static class CasLock {
AtomicReference<Thread> lock = new AtomicReference<>();
public void lock() {
Thread thread = Thread.currentThread();
if (lock.get() == null) { //拿到泛型中Thread的值进行比较
System.out.println(thread.getName() + "----> 开始自旋...");
}
while (lock.compareAndSet(null, thread)) {
}
}
public void unLock() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "----> 解锁成功!");
//解锁
lock.compareAndSet(thread, null);
}
}
走进JUC的世界的更多相关文章
- [C#] 走进 LINQ 的世界
走进 LINQ 的世界 序 在此之前曾发表过三篇关于 LINQ 的随笔: 进阶:<LINQ 标准查询操作概述>(强烈推荐) 技巧:<Linq To Objects - 如何操作字符串 ...
- 走进缓存的世界(三) - Memcache
系列文章 走进缓存的世界(一) - 开篇 走进缓存的世界(二) - 缓存设计 走进缓存的世界(三) - Memcache 简介 Memcache是一个高性能的分布式内存对象缓存系统,用于动态Web应用 ...
- 小丁带你走进git的世界三-撤销修改
一.撤销指令 git checkout还原工作区的功能 git reset 还原暂存区的功能 git clean 还没有被添加进暂存区的文件也就是git还没有跟踪的文件可以使用这个命令清除他们 g ...
- 小丁带你走进git的世界二-工作区暂存区分支
小丁带你走进git的世界二-工作区暂存区分支 一.Git基本工作流程 1.初始化一个仓库 git init git clone git仓库分为两种情况: 第一种是在现有项目或目录下导入所有文件到 ...
- 带你走进rsync的世界
导读 Rsync(remote synchronize)是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件,也可以使用 Rsync 同步本地硬盘中的不同目录.rsync共有3种使用方 ...
- 小丁带你走进git的世界三-撤销修改(转)
一.撤销指令 git checkout还原工作区的功能 git reset 还原暂存区的功能 git clean 还没有被添加进暂存区的文件也就是git还没有跟踪的文件可以使用这个命令清除他们 g ...
- C#7.2——编写安全高效的C#代码 c# 中模拟一个模式匹配及匹配值抽取 走进 LINQ 的世界 移除Excel工作表密码保护小工具含C#源代码 腾讯QQ会员中心g_tk32算法【C#版】
C#7.2——编写安全高效的C#代码 2018-11-07 18:59 by 沉睡的木木夕, 123 阅读, 0 评论, 收藏, 编辑 原文地址:https://docs.microsoft.com/ ...
- 【SpringMVC学习02】走进SpringMVC的世界
上一篇博文主要介绍了springmvc的整个架构执行流程,对springmvc有了宏观上的把握,这一篇博文主要以案例作为驱动,带你走进springmvc的世界.案例中的需求很简单:对商品列表的查询.表 ...
- 【MyBatis学习02】走进MyBatis的世界
mybatis是个持久层的框架,用来执行数据库操作的,无外乎增删改查,上一节对mybatis有了宏观上的了解后,这一篇博客主要通过一个小示例来入门mybatis,先看一下要写的示例需求: 根据用户id ...
随机推荐
- keepalived yum安装后启动报错解决
[root@centos8 ~]yum install keepalived -y [root@centos8 ~]systemctl start keepalived.services [root@ ...
- Kotlin笔记小结(For Java Developer)
这篇文章为kotlin学习记录,主要针对的是自己的知识盲区,不适用于新手. 文中所有demo均来自于kotlin官网 类型 整形 Type Size (bits) Min value Max valu ...
- LGP4456题解
我就是不用矩阵快速幂! 题意:一个 \(\rm 01\) 序列为合法的当且仅当没有两个相邻的 \(1\),若 \(1\) 的个数为 \(x\),\(0\) 的个数为 \(y\),这个 \(\rm 01 ...
- LGP7890题解
前置芝士的光速幂技巧. 本题解不是正解,和正解唯一的差别在于对幂的处理. 我们能够发现有: \[F(n,m,k)=\frac 1 n \binom {n+m-1} m \] 证明见这里. 然后我们开始 ...
- Linux卸载源码编译安装的软件
使用auto-apt 和 checkinstall,具体命令如下 #安装auto-apt和checkinstall apt install auto-apt checkinstall #在源码目录中 ...
- 堆优化Dijkstra算法
但是,我们会发现刚刚讲的朴素Dijkstra算法(高情商:朴素 : 低情商: 低效)的套路不适用于稀疏图,很容易会爆时间: 所以,我们要对其中的一些操作进行优化,首先我们发现找到里起始点最近的点去更新 ...
- Wireshark OpenFlow解析器拒绝服务漏洞
受影响系统:Wireshark Wireshark 2.2.0 - 2.2.1Wireshark Wireshark 2.0.0 - 2.0.7描述:CVE(CAN) ID: CVE-2016-937 ...
- 用iptables封杀内网的bt软件
我所在的网络情况是这样的!1台FC3和3台win2000组成一个局域网!四台机都接在100m的交换机上.在FC3上有两个网卡eth0接外网 adsl eth1接在交换机.FC3做nat带3台win20 ...
- Git 工作流简介
1.概述 工作流有各式各样的用法,但也正因此使得在实际工作中如何上手使用增加了难度.这篇指南通过总览公司团队中最常用的几种 Git 工作流让大家可以上手使用. 在阅读的过程中请记住,本文中的几种工作流 ...
- 让编程更轻松的 7 个 Visual Studio 扩展 : 以下几个扩展,BuildVision可以用
是时候升级你最喜欢的IDE了!在这篇文章中,我将介绍一些我最喜欢的与众不同的 Visual Studio 扩展,是它们让我的日常编程工作变得更加轻松.对于一些明摆着的,例如 ReSharper 和 O ...