1、什么是JUC
2、线程和进程
3、并发和并行
5、生产者消费者问题
6、8锁现象
8、Callable
9、常用的辅助类(CountDownLatch、CyclicBarrier、Semaphore)
JUC第二篇
1、 什么是JUC
juc 就是java.util.concurrent
java.uti工具包、包、分类
业务:普通的线程代码 Thread
Runable 没有返回值、效率相比 Callable 相对较低!

2、进程和线程:
线程、进程、解释;
进程:—个程序,QQ.exe Music.exe程序的集合;
一个进程往往可以包含多个线程,至少包含—个!
Java默认有几个线程?2个mian、GC
那么我们说说线程是什么?例子说明;
线程:开了—个进程Typora ,写字,自动保存(线程负责的)
对于Java而言: Thread、Runnable、Callable
java真的可以开启线程吗?
开不了的;java没有权开启线程的;
为什么开启不了?
虽然我们 new一个start但是我们开启线程的是本地方法start0( )这个方法开启的;native;直接调用本地的c++程序;
- 就是在start里面的start0()方法的源码;
public class Demo01 {
public static void main(String[] args) {
new Thread().start();
}
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//我们调用start方法其实里面直接调用的是start0方法java无法直接操作
//硬件,因为java运行在JVM上的;
private native void start0();
3、 并发和并行
并发、并行
并发(多个线程操作同一个资源)
- CPU一核的,模拟出来多个线程,也就是交替执行
并行(多个人一起行走);提高线程,我们可以使用线程池的技术
- CPU是多核的,多个线程共同执行;
public class Demo01 {
public static void main(String[] args) {
/*new Thread().start();*/
//快速查看能运行多少个线程;也就是获取CPU的核数
//CPU 密集型、IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
结果是12;
这个结果说,我可以运行12个线程;
并发编程的本质:充分利用CPU的资源
线程有几个状态 :
直接看源码:
public enum State {
//线程新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,这种等待会死死地等
WAITING,
//超时等待,这种等待等到一定时间就会不等你了
TIMED_WAITING,
//终止;terminated
TERMINATED;
}
里面有6中状态;
wait/sleep区别 :
1、来自不同的类 :
wait=>Object
sleep =>thread
(其实企业中很少用sleep而是使用juc中的TimeUnit.DAYS.sleep(1); 引用的是import java.util.concurrent.TimeUnit;)
2、关于锁的释放:
wait会释放锁,
sleep睡觉了,抱着锁睡觉,不会释放!
3、使用的范围
同步代码块指在代码块前加上 synchronized关键字的代码块。
同步代码块的格式:
synchronized(同步对象){
需要同步的代码;
}
wait:必须是在同步代码块中
sleep:可以在任何地方睡觉
4、是否需要捕获异常
wait不需要捕获异常
sleep必须要捕获异常
注意 :线程都需要有异常就是中断异常
5、锁
1、synchronized、传统锁
/**
* 一个真正的多线程开发,公司中
* 线程就是一个单独的资源类,没有任何复数的操作!
* 1、在资源类中就是有 属性和方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
//并发就是把资源类丢入线程
//但是在线程里面有一个Runable接口,里面
// @FunctionalInterface 函数是接口
//里面new一个函数接口,然后实行里面的方法,这个就是
//匿名内部类;
/**
* 但是我们写匿名内部类太复杂了;我们可以是使用
* lambda 简化一下;(写参数的)->{ 代码}这个就行;直接可以简化内部匿名的格式
*/
new Thread(()->{
for (int i = 1; i <60 ; i++) {
ticket.sale();
} },"A").start();
new Thread(()->{ for (int i = 1; i <60 ; i++) {
ticket.sale();
} },"B").start();
new Thread(()->{ for (int i = 1; i <60 ; i++) {
ticket.sale();
}},"C").start();
}
}
//资源类oop
class Ticket {
//属性和方法
private int number = 60;
public void sale()
{
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
}
}
}
结果:
没有加锁的情况下:
A卖出了7票,剩余6
A卖出了6票,剩余5
C卖出了18票,剩余17
C卖出了4票,剩余3
C卖出了3票,剩余2
B卖出了19票,剩余18
C卖出了2票,剩余1
A卖出了5票,剩余4
B卖出了1票,剩余0
添加一个synchronized
就是在资源的方法上添加一个锁;
synchronize锁本质就是:队列、锁就机制
//资源类oop
class Ticket {
//属性和方法
private int number = 60;
public synchronized void sale()
{
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
}
}
}
A卖出了13票,剩余12
A卖出了12票,剩余11
A卖出了11票,剩余10
A卖出了10票,剩余9
A卖出了9票,剩余8
A卖出了8票,剩余7
A卖出了7票,剩余6
A卖出了6票,剩余5
A卖出了5票,剩余4
A卖出了4票,剩余3
A卖出了3票,剩余2
A卖出了2票,剩余1
A卖出了1票,剩余0
结果很正规;
2、lock的接口
如果我们是用的话肯定是使用里面的实现类;

1、第一个我们使用的是重入锁:
公平锁∶十分公平∶可以先来后到
非公平锁:十分不公平:可以插队(默认)
使用的步骤:
- 1\首先需要new 一个锁
- 2、然后加锁 对象.lock
- 3\使用try catch fially{解锁}语句进行
public class SaleTicketDemo02 {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
//并发就是把资源类丢入线程
//但是在线程里面有一个Runable接口,里面
// @FunctionalInterface 函数是接口
//里面new一个函数接口,然后实行里面的方法,这个就是
//匿名内部类;
/**
* 但是我们写匿名内部类太复杂了;我们可以是使用
* lambda 简化一下;(写参数的)->{ 代码}这个就行;直接可以简化内部匿名的格式
*/
new Thread(()->{
for (int i = 1; i <60 ; i++) ticket.sale();},"A").start();
new Thread(()->{ for (int i = 1; i <60 ; i++) {
ticket.sale();
} },"B").start();
new Thread(()->{ for (int i = 1; i <60 ; i++) {
ticket.sale();
}},"C").start();
}
}
/**
* 1\首先需要new 一个锁
* 2、然后加锁 对象.lock
* 3\使用try catch fially{解锁}语句进行
*/
class Ticket2 {
//属性和方法
private int number = 60;
Lock lock= new ReentrantLock();
public void sale()
{
//加锁操作:
lock.lock();
try {
//业务代码
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//解锁操作
lock.unlock();
}
}
}
总结:其实和前面的都差不多就是在资源里面的锁的机制改变了;
3、synchronized 和lock的区别:
1、Synchronized内置的Java关键字,Lock是一个Java类(java接口)
2、Synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁
//尝试获取所;返回值是boolean类型的
System.out.println(lock.tryLock());
3.Synchronized会自动释放锁,lock必须要手动释放锁!如果不释放锁,死锁
4、synchronized线程1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去;lock.tryLock()我们可以在这个里面放入时间;等待到一定时间就会放弃了
5、Synchronized可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置);
6.Synchronized适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
5、生产者和消费者
面试的:单例模式、排序算法、生产者和消费者、死锁
public class PC {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//资源类
//步骤就是传统的synchronized
// 1判断,但是两个线程还好,如果线程比较多的话if就不行了,因为判断一次就出来了。用while
// 2如果判断成功的话等待
// 3,不成功的话+1操作然后通知其他线程
class Data{
//属性和方法
private int number=0;
//+1操作
public synchronized void increment() throws InterruptedException {
if (number!=0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"剩余的"+number);
//通知其他线程
this.notifyAll();
}
//减一操作
public synchronized void decrement() throws InterruptedException {
if (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"剩余的"+number);
//通知其他线程
this.notifyAll();
}
}
结果:
A剩余的1
B剩余的0
C剩余的1
D剩余的0
B剩余的-1
B剩余的-2
B剩余的-3
很明显出现了虚假唤醒的问题:
就是在多个线程中可能会出现虚假唤醒的问题;我们需要把if改成while;

线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒
就是线程其实一多在使用if就不行了,因为if只能判断一次就出来了;那么我们需要使用能进行多次判断的,使用while就解决这个问题了;
解决之后:
结果:
D剩余的0
C剩余的1
D剩余的0
C剩余的1
D剩余的0
C剩余的1
D剩余的0
2、JUC版的生产者消费者问题
通过lock找到condition ( ) 这个方法
根据和synchronized和lock的比较

代码实现:
public class PC01 {
public static void main(String[] args) {
Data1 data = new Data1();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
//资源类
//步骤就是lock
// 1判断,但是两个线程还好,如果线程比较多的话if就不行了,因为判断一次就出来了。用while
// 2如果判断成功的话等待这个地方就是需要一个condition进行的等待还有通知所有
// 3,不成功的话+1操作然后通知其他线程
class Data1{
//属性和方法
private int number=0;
//new一个锁
Lock lock= new ReentrantLock();
Condition condition = lock.newCondition();
//+1操作
public void increment() throws InterruptedException {
lock.lock();
try {
while (number!=0){
//等待的
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"剩余的"+number);
//通知其他线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解除锁
lock.unlock();
}
}
//减一操作
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"剩余的"+number);
//通知其他线程
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
结果还是一样的,都可以成功的;
还有个地方就是lock的使用的比起传统的代码需要try包裹起来;我认为安全性会更高一些;
一个新的技术绝对不是,覆盖原来的技术,优势和补充的
condition 精准的通知和唤醒线程 :

没有顺序可言的;我们想让他依次执行可以吗?
答案是;可以的完全可以的;
代码:
注意:
新版的监视器;一个监视器可以监视一个类;
监视器也就是condition
还有个细节就是使用的是同一把锁;所有我们每次只能进来一个;
线程condition调用的问题就是;你想使用那个资源里的方法,你就使用condition监控那个方法的监视器,给那个线程一个通知就行了;但是里面的等待还是那个监控器控制的线程;
try {
//代码
while (number!=2){
condition2.await();
}
number=3;
System.out.println(Thread.currentThread().getName() + "=>bbbbbbb");
condition3.signal();
try {
//代码
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "CCCC");
number=1;
condition1.signal();
/**
* 线程可以让它根据我们的顺序进行
*/
public class PC02 {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
data.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
data.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i <10 ; i++) {
data.printC();
}
},"C").start();
}
}
class Data3{
Lock lock=new ReentrantLock();
private int number=1;
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void printA()
{
lock.lock();
try {
//代码
while (number!=1){
condition1.await();
}
number=2;
System.out.println(Thread.currentThread().getName()+"=>AAAAA");
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB()
{
lock.lock();
try {
//代码
while (number!=2){
condition2.await();
}
number=3;
System.out.println(Thread.currentThread().getName() + "=>bbbbbbb");
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC()
{
lock.lock();
try {
//代码
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "CCCC");
number=1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
6、 8锁现象
如何判断锁的是谁;永远知道什么是锁,锁到底锁的是谁;
1、现象;两个都有锁的方法;线程先调用那个?
/**
* 8种锁的现象,就是关于所的8个现象
* 1、标准情况下,两个线程先打印 发短信 还是打电话? 1/发短信 2、打电话
*发短信
*/
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
Phone phone = new Phone();
new Thread(()->{
phone.sendSms();
},"A").start();
// 延迟一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
},"A").start();
}
}
class Phone{
public synchronized void sendSms()
{
System.out.println("发短信");
}
public synchronized void call()
{
System.out.println("打电话");
}
}
总结 :我们使用我的是锁的对象是方法的调用者!
但是资源里面的两个方法用的是同一个锁都是使用new Phone()这个对象,这个对象谁先拿到对象调用的方法就是谁先执行;
2、现象;一个是带锁;另一个不带锁(也就是说没有锁的方法),那么线程现调用哪一个呢?
public class Demo03 {
public static void main(String[] args) throws InterruptedException {
Phone3 phone = new Phone3();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
// 延迟一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
},"B").start();
}
}
class Phone3{
public synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public void hello()
{
System.out.println("hello");
}
}
结果:这个东西不受锁的影响;同样是一个对象调用;虽然谁先调用。还是先运行谁的方法,但是里面的hello是不受锁的影响的;一旦锁的进行sleep了,那么hello方法就会执行;
3、问题两个对象调用不同的方法,方法都是加锁的,那么会执行那个;一个方法中使用睡眠4秒,另一个没有睡眠
public class Demo03 {
public static void main(String[] args) throws InterruptedException {
Phone3 phone = new Phone3();
Phone3 phone1=new Phone3();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
// 延迟一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.hello();
},"B").start();
}
}
class Phone3{
public synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public synchronized void hello()
{
System.out.println("hello");
}
}
结果:肯定先执行没有睡觉的那个方法;因为是两个不同的对象嘛;谁先枪住谁执行的;没有延迟没有办法玩;
4、在锁的前面添加一个static 那个那么一个先执行;
public class Demo04 {
public static void main(String[] args) throws InterruptedException {
Phone4 phone = new Phone4();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
// 延迟一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
},"B").start();
}
}
class Phone4{
public static synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public static synchronized void hello()
{
System.out.println("hello");
}
}
发短信
hello
结果 :static 标注这个方法肯定是一个静态方法;又因为这个资源类是一个全局唯一,这个地方锁的是class对象;因为这个用的是同一把锁,所以只能谁先拿到,然后执行谁;
5、两个对象还是两个同步方法,还是静态的;那么我们先打印那个?
public class Demo04 {
public static void main(String[] args) throws InterruptedException {
Phone4 phone = new Phone4();
Phone4 phone1 = new Phone4();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
// 延迟一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.hello();
},"B").start();
}
}
class Phone4{
public static synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public static synchronized void hello()
{
System.out.println("hello");
}
}
结果 : 虽然我们使用的是两个对象,如果没有static的话,肯定是没有睡觉的先执行;但是我们添加的是static,这个时候我们锁的是一个Class,那么先执行的肯定是那个对象先调,就先执行那个;
6、一个是静态加锁另一个是直接加锁,这个是一个对象的,先执行哪一个
public class Demo04 {
public static void main(String[] args) throws InterruptedException {
Phone4 phone = new Phone4();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
// 延迟一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.hello();
},"B").start();
}
}
class Phone4{
public static synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public synchronized void hello()
{
System.out.println("hello");
}
}
结果 : 先分析就是静态加锁 锁的是class类,而另一个锁,并不是锁的Class,所以两个是不相同的锁,所以互不干扰;谁快谁就会先输出;
7、尽管是两个对象,一个是静态加锁另一个是直接加锁,先执行哪一个
public class Demo04 {
public static void main(String[] args) throws InterruptedException {
Phone4 phone = new Phone4();
Phone4 phone1 = new Phone4();
new Thread(()->{
try {
phone.sendSms();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
// 延迟一秒
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone1.hello();
},"B").start();
}
}
class Phone4{
public static synchronized void sendSms() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("发短信");
}
public synchronized void hello()
{
System.out.println("hello");
}
}
结果 : 先分析就是静态加锁 锁的是class类,而另一个锁,并不是锁的Class,所以两个是不相同的锁,所以互不干扰;谁快谁就会先输出;
7、集合类不安全
因为我们使用的Juc是并发执行的;所以我们以前写的很多list…都不是很安全的;但是单线程确实很安全,但是我们只是使用于我们自己罢了;人一多就完蛋;

这个三个是同级的;
1、 List不安全
如果我们使用一般发的线程使用list集合的时候,会出错!
只要在并发集合下都会出现这个错误 :
java.util.ConcurrentModificationException 并发修改异常
1、在并发下我们看出List list = new ArrayList<>( );是不安全的会出现异常的现象;我们现在的问题该怎么解决这个问题?
有三个方法:
1、List list = new Vector<>(); 我们可以使用vector 默认就是安全的;面试不要回答这个;因为这个在jdk1.0就用了;有点老土了;
2、他不安全我们可以让他变的安全就行了吧!List list = Collections.synchronizedList(new ArrayList<>());
3、List list = new CopyOnWriteArrayList<>();
CopyOnWrite写入时复制;简称cow计算机程序设计领域的优化策略;就是在多个线程调用的时候,list,读取固定,写入(覆盖)用了这个写入的时候避免覆盖,造成了数据问题。
//说到这个时候还会谈到读写分离的机制;mycat 这个是数据库界别的;
4、注意:为什么我们不用vector而用copyonwriteArrayList()有点在哪里呢?
因为vector 在add的时候里面用到了synchronized ;只要用到这个效率就会下降;而copyonwriteArrayList里面用到的是lock锁;里面复制一份然后在写入就行了;
代码:
1、
public class ListTest {
public static void main(String[] args) {
//并发下arraylist 不安全
/**
* 解决方案
*/
List<String> list = new ArrayList<>();
for (int i = 0; i <=10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
2、
public class ListTest {
public static void main(String[] args) {
//并发下arraylist 不安全
/**
* 解决方案
*/
/* List<String> list = new ArrayList<>();*/
/*List<String> list = new Vector<>();*/
/* List<String> list = Collections.synchronizedList(new ArrayList<>());*/
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i <=10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
2、set不安全
1、出现的问题还是并发性异常;
java.util.ConcurrentModificationException并发性修改异常
解决问题:
1、第一种就是使用我们的
Set set= Collections.synchronizedSet(new HashSet<>());
这个可以完整的解决我们的异常问题!collections一个工具类;
2、就是使用:Set set= new CopyOnWriteArraySet<>();
代码:
class HashTet {
public static void main(String[] args) {
/* Set<String> set=new HashSet<>();*/
/* Set<String> set= Collections.synchronizedSet(new HashSet<>());*/
Set<String> set= new CopyOnWriteArraySet<>();
for (int i = 0; i <=10 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
3、hashset的底层是什么:
1、hashset的底层就是hashmap
用的就是hashmap里面的一个key值;因为key值无法重复的!你说底层是hashmap 里面的key就是你set的值,但是你说hashmap里面的value是什么?其实就是一个常量一个不会改变的 private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
add方法就是直接put一个值:
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
4、Map 的不安全

1、有几个问题:
map 是这样使用我的吗?答案不是,工作中并不是这样使用的new HashMap<>();
第一个初始化容量;第二个加载因子
默认的 new HashMap<>(16,0.75);
2、使用的问题还是会出现java.util.ConcurrentModificationException
并发修改异常错误;
解决问题:
1、 Map<String, Object> map=new ConcurrentHashMap<>();
2、使用工具类的
Map<String, Object> map= Collections.synchronizedMap(new HashMap<>());
代码:
public class MapTest {
public static void main(String[] args) {
/**
* map 是这样使用我的吗?答案不是,工作中并不是这样使用的new HashMap<>();
* 第一个初始化容量;第二个加载因子
* 默认的 new HashMap<>(16,0.75);
*/
/* Map<String, Object> map = new HashMap<>();*/
/* Map<String, Object> map=new ConcurrentHashMap<>();*/
Map<String, Object> map= Collections.synchronizedMap(new HashMap<>());
for (int i = 0; i <=10 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
8、Callable

1、callable可以有返回值,
2、可以抛出异常
3、方法不同runnable 是run方法,而callable是 call()


Class FutureTask里面有一个这个类,这个类的改造方法可以和callable挂上关系;我们可以使用;
代码:
public class callableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread thread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(thread);
new Thread(futureTask,"A").start();
//这个能抛出异常还有能打印返回的结果了;通过get方法
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 123;
}
}
还有可能的是结果会产生缓存;还有get方法可能会产生阻塞;就是在返回前面添加时间,那么我们可能会产生阻塞,会一直等待,所以get方法一般都会写到最后面;
public class callableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread thread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(thread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();
//这个能抛出异常还有能打印返回的结果了;通过get方法
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 123;
}
}
结果:
两个线程为什么只有一个结果就是产生的了缓存的效果;
call()
123
总结 :就是根据这个进行的一层调用一层调用一层进行的;还有就是:1、有缓存 2、结果可能需要等待,会阻塞;
常用的辅助类
1、 CountDownLatch
这是一个减法计数器;
代码:
public class CountDown {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "go");
countDownLatch.countDown();//-1操作
}, String.valueOf(i)).start();
}
//等待计数器归零操作之后然后在执行下面的代码
countDownLatch.await();
System.out.println("操作结束了");
}
}
原理:
countDownLatch.countDown();//-1操作
//等待计数器归零操作之后然后在执行下面的代码
countDownLatch.await();
每次有线程调用countDown(); 数量减一操作;,假设计数器变为0,countDownLatch.await();就会别唤醒,继续执行;
2、CyclicBarrier
加法计数器
class CyclicBarrier1 {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(7,()->{
System.out.println("召唤成功了");
});
for (int i = 1; i <=7 ; i++) {
new Thread(()->{
System.out.println("已经收集"+Thread.currentThread().getName());
try {
barrier.await( );//等待结束
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
).start();
}
}
}
其实还有一个问题就循环一个线程的时候我们有什么办法拿到 i 值吗?
其实有的:但是问题的是我们不能直接拿到的,我们需要一个中间的变量;使用的还是final的性的变量;那么问题来了,我们为什么要使用final呢?其实就是我们所说的内部匿名的类比方法的周期要长,所以我们为了保持一致性,我们需要前面添加一个final关键字;
总结 : 就是线程的问题;我们开始new cyclicBarrir之后我们进行for循环,然后我们在进行里面需要使用wait等待,每次循环 ,在计数里面都会加1操作;知道加到固有的参数之后才可以运行;
3、Semaphore
计数的信号量
代码:
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore= new Semaphore(3);
for (int i = 1; i <=6 ; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//进行释放操作
semaphore.release();
System.out.println(Thread.currentThread().getName()+"离开车位");
}
}).start();
}
}
}
原理:
semaphore.acquire(); 获得,假设如果已经满了,等待,等待被释放为止!
semaphore.release(); 释放,会将当前的的信号量释放+1操作,然后唤醒等待的线程!
作用 : 多个共享资源互斥的使用!并发限流,控制最大线程数!
总结 : 就是每一个许可证都是可用的,通过acquire进行进行阻塞操作,然后进行,然后就是使用release进行释放,然后在一次类推;知道循环完结束本次操作;