文章目录
JUC
1.ReentrantLock
a)lock写在try之前
b)一定要记得在finally里面进行unlock()
2.信号量
acquire():void ------>尝试获取锁,如果可以正常获取到,则执行后面的业务逻辑,如果获取失败,则阻塞等待
release():void------>释放锁
//信号量演示
public class ThreadMain98 {
public static void main(String[] args) {
//创建信号量
Semaphore semaphore=new Semaphore(2,true);
ThreadPoolExecutor executor=new ThreadPoolExecutor(10,10,
0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(100));
for (int i = 0; i <4 ; i++) {
//创建任务
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"到达停车场");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//试图进入停车场
try {
//尝试获取锁
semaphore.acquire();
//当代码只想到此处,说明已经获取到锁
System.out.println(Thread.currentThread().getName()+"进入停车场---------");
//车辆停留的时间
int num=1+new Random().nextInt(5);
try {
Thread.sleep(num*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"离开停车场...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//释放锁
semaphore.release();
}
}
});
}
}
}
3.计数器:CountDownLauth
计数器是用来保证一组线程同时完成某个操作之后,才能继续之后的任务。
await():void-----等待(当线程数量不满足CountDownLauth的数量的时候,会执行此代码会阻塞等待,直到数量满足之后,在执行await之后的代码。)
countDown():void
CountDownLauth是如何实现的?
答:CountDownLauth里面有一个计数器,计数器的数量在初始化的时候设置,每次调用countDown()方法的时候,计数器的数量-1,直到减到0之后,就可以执行await之后的代码。
import java.util.concurrent.CountDownLatch;
/**
* 计数器
*/
public class ThreadMain99 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch=new CountDownLatch(5);
for (int i = 1; i <6 ; i++) {
int finaI=i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始起跑");
try {
Thread.sleep(finaI*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"到达终点");
//将计数器-1
latch.countDown();
}
}).start();
}
//阻塞等待
latch.await();
System.out.println("所有人都到达终点了,公布排名");
}
}
声明计数器初始值为5
当计数器减到0之后,在执行await
CountDownLauth缺点
CountDownLauth计数器的使用是一次性的,当用完一次之后就不能使用了
4.循环屏障(循环栅栏):CyclicBarrier
await():int ------等待(CyclicBarrier里面有一个计数器,每次执行await方法的时候,计数器的数量-1,直到减到0之后,就可以执行await之后的代码,并且此时会将count值(计数器的值)重置继续利用。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* 循环屏障
*/
public class ThreadMain100 {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier=new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("执行了CyclicBarrier里面的Runnable");
}
});
for (int i = 1; i < 11; i++) {
int finalI=i;
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始起跑");
try {
Thread.sleep(finalI*200);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println(Thread.currentThread().getName()+"等待其他人-----------");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
//代码执行到此行,说明已经有一组代码已经满足情况
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}).start();
}
}
}
循环屏障的使用:
1.计数器-1
2.判断计数器是否为0,如果为0执行之后的代码,如果不为0阻塞等待
当计数器为0时,首先会执行await之后的代码,将计数器重置
CyclicBarrier和CountDownLatch区别?
答:CountDownLatch计数器只能使用一次,CyclicBarrier他的计数器可以重复使用。
HashMap
HashMap底层实现结构,负载因子,哈希冲突的解决等…线程问题
HashMap------非线程安全的容器
——JDK1.7死循环(要会画出来)
——JDK1.8数据覆盖
HashMap—>数组+链表 / 红黑树
HashMap 负载因子(加载因子)0.75 扩容:16*0.75=进行扩容
0.75:尽量的避免哈希冲突所带来的性能开销问题(空间换时间的方案)
HashMap线程安全方案
ConcurrentHashMap
JDK1.7是将ConcurrentHashMap分成几个segment进行加锁(分段锁)。
JDK1.8锁优化:读的时候不加锁,写的时候加锁,使用了大量的CAS,Voiltail…
Hashtable:线程安全的容器
给put方法直接加锁,因此一般情况不会使用Hashtable
HashMap,ConcurrentHashMap,Hashtable区别?
- HashMap是非线程安全的容器,他在JDK1.7造成死循环,JDK1.8会造成数据覆盖,Hashtable和ConcurrentHashMap都是线程安全的。
- Hashtable实现线程安全的手段比较简单,他是在给put方法整体加了一把锁,因此性能不高,所以使用频率比较低;而ConcurrentHashMap是Hash MAp在多线程下的替代方案,他在JDK1.7的时候使用的Lock加分段锁的方案来实现线程安全的保障,而在JDK1.8的时候使用了大量的使用了大量的CAS,Voiltail来实现线程的,并且在JDK1.8读取的时候不加锁(读取的数据可能不是最新,因为读取和写入同时进行),只有在写的时候才加锁。