赞
踩
可重入互斥锁。和synchronized定位类似,都是使用实现互斥效果,保证线程安全。
ReentrantLock的用法:
- import java.util.concurrent.locks.ReentrantLock;
-
- /**
- * Describe:ReentrantLock中lock的使用
- * User:lenovo
- * Date:2023-03-30
- * Time:15:58
- */
- public class TestDemo1 {
- public static ReentrantLock lock = new ReentrantLock();
- public static int count = 0;
-
- public static void main(String[] args) throws InterruptedException {
- Thread t1 = new Thread(() -> {
- lock.lock();
- try {
- for (int i = 0; i < 1000; i++) {
- count++;
- }
- }finally {
- lock.unlock();
- }
- });
- Thread t2 = new Thread(() -> {
- lock.lock();
- try {
- for (int i = 0; i < 1000; i++) {
- count++;
- }
- }finally {
- lock.unlock();
- }
- });
- t1.start();
- t2.start();
- t1.join();
- t2.join();
- System.out.println(count);
- }
- }
ReentrantLock和synchronized的区别:
如何选择那个锁呢?
原子类内部使用的是CAS实现,所以性能要比加锁实现i++高了好多。原子类有:
以AtomicInteger举例,常见的方法有:
addAndGet(int delta); i += delta;
decrementAndGet(); --i;
getAndDecrement(); i--;
incrementAndGet(); ++i;
getAndIncrement(); i++;
- public class TestDemo3 {
- public static void main(String[] args) {
- AtomicInteger a = new AtomicInteger(0);
- System.out.println(a.incrementAndGet());//++a
- System.out.println(a.getAndIncrement());//a++
- System.out.println(a.decrementAndGet());//--a
- System.out.println(a.getAndDecrement());//a--
- }
- }
虽然创建销毁线程比创建销毁进程更轻量,但是在频繁的创建和销毁线程的时候还是会比较低效的
线程池就是为了解决这个问题。如果某个线程不在使用了,并不是真正的把线程释放了,而是放到一个池子里。如果需要用到线程就直接从池子中取,不必通过系统来创建。
【代码示例】
- public class TestDemo4 {
- public static void main(String[] args) {
- ExecutorService pool = Executors.newFixedThreadPool(10);
- pool.submit(new Runnable() {
- @Override
- public void run() {
- System.out.println("hello");
- }
- });
- }
- }
Executors创建线程池的几种方式
Executors本质上是ThreadPoolExecutor类的封装。
ThreadPoolExecutor提供了更多的可选参数,可以进一步细化线程池行为的设定。
【构造方法】
理解ThreadPoolExecutor构造方法的参数
把创建一个线程可以想象为开个公司,每个员工相当于一个线程。
【拒绝策略】
- public class TestDemo5 {
- public static void main(String[] args) {
- ExecutorService pool = new ThreadPoolExecutor(4, 8, 1000, TimeUnit.MICROSECONDS,
- new SynchronousQueue<Runnable>(),
- Executors.defaultThreadFactory(),
- new ThreadPoolExecutor.AbortPolicy());
- for (int i = 0; i < 9; i++) {
- pool.submit(new Runnable() {
- @Override
- public void run() {
- System.out.println("hello");
- }
- });
- }
- }
- }
信号量:用来表示可用资源的个数。本质上是一个计数器。
【理解信号量】可以把信号量理解为停车场的指示牌:当前由100个车位,表示有100个车位是空闲的。当有一辆车进入的时候,相当于申请一个资源,数量就会-1;当有一辆车出来的时候,相当于释放资源,数量就会+1。同样的栗子,有火车票剩余数量,酒店空闲房间等。
Semaphore的PV操作中加减计数器操作都是原子的,可以在多线程的环境下使用。
【代码示例】
- public class TestDemo6 {
- public static void main(String[] args) {
- Semaphore semaphore = new Semaphore(1);
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- try {
- System.out.println("申请资源");
- semaphore.acquire();
- System.out.println("获取到资源了");
- Thread.sleep(1000);
- semaphore.release();
- System.out.println("释放资源了");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- };
- for (int i = 0; i < 20; i++) {
- Thread t = new Thread(runnable);
- t.start();
- }
- }
-
-
- }
同时等待N个任务执行结束
【举个栗子】好像跑步比赛,10个选手依次就位,哨声响起同时出发;所有选手到达终点,比赛此结束。
- public class TestDemo7 {
- public static void main(String[] args) throws InterruptedException {
- CountDownLatch latch = new CountDownLatch(10);
- Runnable r = new Runnable() {
- @Override
- public void run() {
- System.out.println("hello");
- try {
- Thread.sleep(1000);
- latch.countDown();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- };
- for (int i = 0; i < 10; i++) {
- new Thread(r).start();
- }
- latch.await();
- System.out.println("比赛结束");
- }
- }
1)线程同步的方式有哪些?
synchronized,ReentrantLock,Semaphore等都可以用于线程同步。
2)为什么有了synchronized,还需要juc下的lock?
以juc的ReentrantLock为例,
- synchronized使用时不需要手动释放锁。ReentrantLock需要手动释放锁,使用起来更灵活。
- synchronized在申请锁失败的时候,会死等。ReentrantLock可以通过trylock的方式等待一段时间就放弃了。
- synchronized是非公平锁。ReentrantLock默认是非公平锁,可以通过构造方法传入一个true开启公平锁模式。
- synchronized是通过Object的wait/notify实现等待唤醒。每次唤醒的是一个随机等待的线程。ReentrantLock搭配Condition类实现等待-唤醒,更加精确控制某个指定的线程
3)AtomicInteger实现的原理是什么?
基于CAS机制来实现
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。