JUC是什么?java线程基础知识
JUC是什么?
JUC,即java.util.concurrent包的缩写,是java原生的并发包和一些常用的工具类。
线程基础知识
(资料图片)
线程和进程进程:计算机中运行中的程序,如QQ.exe等。 线程:进程中执行的具体的任务,如打字、自动保存等。 一个进程可以包含多个线程,一个进程至少有一个线程。Java程序至少有两个线程:GC线程和Main线程。 并发和并行并发:多个线程操作同一个资源并且交替执行的过程。 并行:多个线程同时执行,只有在多核CPU下才能完成。 使用多线程或者并发编程的目的:提高效率,让CPU一直工作,达到最高的处理性能。线程的状态线程有6种状态,我们可以从源码中查看具体是哪6种状态。
public enum State { // java能够创建线程吗? 不能! // 新建 NEW, // 运行 RUNNABLE, // 阻塞 BLOCKED, // 等待 WAITING, // 延时等待 TIMED_WAITING, // 终止! TERMINATED;}
很显然,线程的六种状态分别是:新建(NEW)、运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITTING)、延时等待(TMED_WAITTING)、终止(TERMINATED)。wait和sleep的区别
类不同 wait是属于Object类的方法,sleep是Thread类的方法。在JUC编程中,线程休眠的实现代码是:
TimeUnit.SECONDS.sleep(3)
是否会释放资源 sleep会一直持有锁,不会释放锁,wait则会释放锁。使用范围不同 wait和notify是一组,一般在线程通信的时候使用。sleep是单独的方法,在任何地方都可以使用。是否需要捕获异常 sleep需要捕获中断异常,wait不需要。
Lock锁
传统方式一般采用synchronized关键字来加锁,如以下代码:
package com.coding.demo01;// 传统的 Synchronized// Synchronized 方法 和 Synchronized 块/* * 我们的学习是基于企业级的开发进行的; * 1、架构:高内聚,低耦合 * 2、套路:线程操作资源类,资源类是单独的 */public class Demo01 { public static void main(String[] args) throws InterruptedException { // 1、新建资源类 Ticket ticket = new Ticket(); // 2、线程操纵资源类 new Thread(new Runnable() { public void run() { for (int i = 1; i <=40; i++) { ticket.saleTicket(); } } },"A").start(); new Thread(new Runnable() { public void run() { for (int i = 1; i <=40; i++) { ticket.saleTicket(); } } },"B").start(); new Thread(new Runnable() { public void run() { for (int i = 1; i <=40; class="" private="" int="" number="30;">close=> public synchronized void saleTicket(){ if (number>0){ System.out.println(Thread.currentThread().getName() + "卖出第"+(number--)+"票,还剩:"+number); } }}
现在,我们也可以使用Lock来加锁。
Lock lock=new ReentrantLock()
ReentrantLock,即可重入锁(相当于回家的时候只要开了大门的锁,卧室,厕所不需要解锁就能进入),其默认是非公平锁(不公平,后面的线程可以插队)。如以下代码:
package com.coding.demo01;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/* * JUC之后的操作 * Lock锁 + lambda表达式! */public class Demo02 { public static void main(String[] args) { // 1、新建资源类 Ticket2 ticket = new Ticket2(); // 2、线程操作资源类 , 所有的函数式接口都可以用 lambda表达式简化! // lambda表达式 (参数)->{具体的代码} new Thread(()->{for (int i = 1; i <= 40="" new="" -="">{for (int i = 1; i <= 40="" new="" -="">{for (int i = 1; i <= 40="" class="" reentrantlock="" :="" private="" lock="new" int="" number="" public="" void="" try="" if="">0){ System.out.println(Thread.currentThread().getName() + "卖出第"+(number--)+"票,还剩:"+number); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); // 解锁 } }}
synchronized和Lock的区别1.synchronized是一个关键字,Lock是一个对象。 2.synchronized无法尝试获取锁,Lock可以尝试获取锁并判断。 3.synchronized会自动释放锁(a线程执行完毕,b如果出现异常也会释放锁),Lock锁必须手动进行释放,不释放就会变成死锁。 4.使用synchronized时,如果线程a获得锁并阻塞,线程b会一直进行等待,使用Lock则可以尝试获取锁,失败了之后就放弃。
5.synchronized一定是非公平的,但Lock锁可以是公平的,需要通过参数进行设置。 6.代码量特别大时,一般使用Lock实现精准控制,synchronized适合代码量较小的同步问题。
生产者消费者问题
线程和线程之间本来是不能通信的,但有时我们需要线程之间进行协调操作。 比如有两个线程:A、B ,还有一个值初始为0,实现两个线程交替执行,对该变量 + 1,-1;交替10次。 先来看使用synchronized实现线程之间通信的版本,代码如下:
package com.coding.demo01;// Synchronized 版/*目的: 有两个线程:A B ,还有一个值初始为0, 实现两个线程交替执行,对该变量 + 1,-1;交替10次 */public class Demo03 { public static void main(String[] args) { Data data = new Data(); // +1 new Thread(()->{ for (int i = 1; i <=10 try="" catch="" interruptedexception="" -1="" new="" -="">{ for (int i = 1; i <=10 ; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); }}// 资源类// 线程之间的通信: 判断 执行 通知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(); //唤醒所有线程 } // -1 public synchronized void decrement() throws InterruptedException { if (number==0){ // 判断是否需要等待 this.wait(); } number--; // 执行 System.out.println(Thread.currentThread().getName()+""+number); // 通知 this.notifyAll(); //唤醒所有线程 }}
那么问题来了,这四条线程可以实现交替吗?答案是不能!因为会产生虚假唤醒问题,jdk文档中对该问题也有说明。
需要特别注意的if和while的区别,当两个线程同时执行if判断,if只会判断一次,而while会对每一个线程都进行判断。显然,上面的if应该改为while,代码如下:
package com.coding.demo01;// Synchronized 版/*目的: 有两个线程:A B ,还有一个值初始为0, 实现两个线程交替执行,对该变量 + 1,-1;交替10次 传统的 wait 和 notify方法不能实现精准唤醒通知! */public class Demo03 { public static void main(String[] args) { Data data = new Data(); // +1 new Thread(()->{ for (int i = 1; i <=10 try="" catch="" interruptedexception="" new="" -="">{ for (int i = 1; i <=10 try="" catch="" interruptedexception="" -1="" new="" -="">{ for (int i = 1; i <=10 try="" catch="" interruptedexception="" new="" -="">{ for (int i = 1; i <=10 ; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); }}// 资源类// 线程之间的通信: 判断 执行 通知class Data{ private int number = 0; // +1 public synchronized void increment() throws InterruptedException { while (number!=0){ // 判断是否需要等待 this.wait(); } number++; // 执行 System.out.println(Thread.currentThread().getName()+""+number); // 通知 this.notifyAll(); //唤醒所有线程 } // -1 public synchronized void decrement() throws InterruptedException { while (number==0){ // 判断是否需要等待 this.wait(); } number--; // 执行 System.out.println(Thread.currentThread().getName()+""+number); // 通知 this.notifyAll(); //唤醒所有线程 }}
问题又来了,从测试的结果可以看出,传统的 wait 和 notify方法不能实现精准唤醒通知。 这时我们就需要考虑使用JUC来实现了,先来看看JUC中的一个重要的接口Condition的文档说明。
我们使用Lock锁和Condition来实现精准唤醒线程,代码如下:
package com.coding.demo01;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*实现线程交替执行!主要的实现目标:精准的唤醒线程! 三个线程:A B C 三个方法:A p5 B p10 C p15 依次循环 */public class Demo04 { public static void main(String[] args) { Data2 data = new Data2(); new Thread(()->{ for (int i = 1; i <= try="" catch="" interruptedexception="" new="" -="">{ for (int i = 1; i <= try="" catch="" interruptedexception="" new="" -="">{ for (int i = 1; i <= 10; i++) { try { data.print15(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); }}// 资源类class Data2{ private int number = 1; // 1A 2B 3C private Lock lock = new ReentrantLock(); // 实现精准访问 private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); public void print5() throws InterruptedException { lock.lock(); try { // 判断 while (number!=1){ condition1.await(); } // 执行 for (int i = 1; i <= 5; i++) { System.out.println(Thread.currentThread().getName() + "" + i); } // 通知第二个线程干活! number = 2; condition2.signal(); // 唤醒 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); // 一定要解锁 } } public void print10() throws InterruptedException { lock.lock(); try { // 判断 while (number!=2){ condition2.await(); } // 执行 for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName() + "" + i); } // 通知3干活 number = 3; condition3.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void print15() throws InterruptedException { lock.lock(); try { // 判断 while (number!=3){ condition3.await(); } // 执行 for (int i = 1; i <= 15; i++) { System.out.println(Thread.currentThread().getName() + "" + i); } // 通知 1 干活 number = 1; condition1.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }}
测试结果说明,使用Lock锁很容易就解决上述问题,由此我们可以得到一个结论:一个新技术的出现,一定是为了替换一些旧的技术的!
锁对象的判断方法
1.被synchronized修饰的方法,锁的对象是方法的调用者,当两个方法调用的对象是同一个时,先调用的先执行。 2.没有被synchronized修饰的方法,不是同步方法,不受锁的影响。 3.只要方法被static修饰,不管是否同时被synchronized修饰,锁的对象就是Class模板对象,这个对象是全局唯一的。 4.synchronized锁的是调用的对象,static锁的是这个类的Class模板,这是两个不同的锁。
不安全的集合类
只要在并发环境下,List、Map、Set这些类都是不安全的。 List不安全的代码示例:
package com.coding.unsafe;import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;/** * 故障现象:ConcurrentModificationException 并发修改异常 * 导致原因:add方法没有锁! * 解决方案: * 1、List list = new Vector<>(); //jdk1.0 就存在的!效率低 * 2、List list = Collections.synchronizedList(new ArrayList<>()); * 3、List list = new CopyOnWriteArrayList<>(); * * 什么是 CopyOnWrite; 写入是复制 (思想 COW) * 多个调用者同时要相同的资源;这个有一个指针的概念。 * 读写分离的思想: */public class UnSafeList { public static void main(String[] args) {// List list = Arrays.asList("a", "b", "c");// list.forEach(System.out::println);// List list = new ArrayList<>(); List list = new CopyOnWriteArrayList<>(); for (int i = 1; i <= new="" -="">{ list.add(UUID.randomUUID().toString().substring(0,3)); System.out.println(list); },String.valueOf(i)).start(); } }}
如上述代码所示,解决List不安全问题的方法有两种:
List list = Collections.synchronizedList(new ArrayList<>());List list = new CopyOnWriteArrayList<>();
CopyOnWrite(COW),写入是复制,多个调用者同时要相同的资源,这是一种读写分离的思想,其源码如下:
public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); }}
Set不安全的代码示例:
package com.coding.unsafe;import java.util.Collections;import java.util.HashSet;import java.util.Set;import java.util.UUID;import java.util.concurrent.CopyOnWriteArraySet;// ConcurrentModificationExceptionpublic class UnSafeSet { public static void main(String[] args) { // HashSet 底层是什么 就是 HashMap // add,就是 HashMap 的 key; Set set = new HashSet<>();// Set set = Collections.synchronizedSet(new HashSet<>());// Set set = new CopyOnWriteArraySet(); for (int i = 1; i <=30 new="" -="">{ set.add(UUID.randomUUID().toString().substring(0,3)); System.out.println(set); },String.valueOf(i)).start(); } }}
如上述代码所示,解决Set不安全问题的方法有两种:
Set set = Collections.synchronizedSet(new HashSet<>());Set set = new CopyOnWriteArraySet();
Map不安全的代码示例:
package com.coding.unsafe;import java.util.HashMap;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap;//ConcurrentModificationExceptionpublic class UnsafeMap { public static void main(String[] args) { // new HashMap<>() 工作中是这样用的吗? 不是 // 加载因子0.75f;,容量 16; 这两个值工作中不一定这样用! // 优化性能! // HashMap 底层数据结构,链表 + 红黑树 // = = = = = = =// Map map = new HashMap<>(); Map map = new ConcurrentHashMap<>(); // 人生如程序,不是选择就是循环,时常的自我总结十分重要! for (int i = 1; i <=30 new="" -="">{ map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,3)); System.out.println(map); },String.valueOf(i)).start(); } }}
解决Map不安全问题的方法是使用ConcurrentHashMap来替代HashMap:
Map map = new ConcurrentHashMap<>();
综上所述,要解决一般集合的线程不安全的问题,核心思路就是使用JUC并发包下面的并发安全的集合去替代这些不安全的集合。
标签:
相关推荐:
最新新闻:
- 双击我的电脑打不开怎么办?教你快速处理方法
- Windows8:什么语言在未来不会被淘汰?
- 新风系统到底应该怎么装?新风能解决什么问题?
- 全球首家!中国电信“全网通”手机来了|世界热门
- 播报:InstallPackage报错怎么办?解析channel_v3.json文件出现的问题
- JSP基础教程:jspcms第一个JSP的扩展名是什么?
- 【安全生产】“GPS/北斗巡检系统”为“安全运营”保驾护航 视点
- 如何安装旗舰版win7?教您win7旗舰版安装教程|天天热头条
- 世界热门:如何打开pdg文件?手把手教你打开PDG文件
- golang-java-serializer序列化算法解析|当前热文
- 哪个品牌的蓝牙耳机质量好?Bose、森海塞尔、索尼、Jabra等蓝牙耳机都不错
- iphone4s怎么换电池?iphone4s更换电池教程-焦点资讯
- 【python】Python中最常用的数据类型|播资讯
- mysql-h主机名-p密码库名C-lamp61数据库
- 基于HDRS平台的仓储远程监控系统——华辰智通
- 如何利用百度引擎进行网站宣传?网站宣传的核心与重点
- 如何申请注册163个人邮箱?163个人邮箱注册申请流程|环球新视野
- 焦点播报:电动车蓄电池坏了怎么办?蓄电池怎样修复?蓄电池修复步骤
- 紫外光激发使光诱导进入二苯并--萘酚的花菁形式
- 如何通过factory-girl去定义测试数据中的用户角色?
- 99宿舍网四六级成绩怎么查询?99宿舍网四六级成绩查询入口
- 启发式搜索算法解决八数码问题(C语言)珍藏版:每日简讯
- 【爬取红岭创投】存储在mysql数据库中的无头模式 当前速讯
- 每日热闻!世界十大杰出物理学家:牛顿、爱因斯坦、麦克斯韦上榜
- 笔记本摄像头驱动怎么安装?笔记本摄像头驱动安装方法
- python中的matplotlib:创建图表的绘图包:世界速看料
- 当前关注:联想y400n怎么样?霸气筋肉车风格联想y400n评测
- ibm笔记本故障如何维修?ibm笔记本故障维修方法 天天简讯
- 2019年运城高考状元名单公布:运城文理科状元是谁?
- 什么是环保硒鼓?使用中出现问题怎么办?|世界快资讯
- JUC是什么?java线程基础知识
- numpy基本教程:Numpy实现数组的逻辑运算_当前观点
- 东芝电视故障如何维修?东芝电视故障及维修方法介绍 全球通讯
- bot短期密集访问形成的流量高峰有哪些?如何解决?|天天微资讯
- 充电风扇哪个牌子好?充电风扇品牌推荐
- 如何通过bios调节风扇转速?合理调节风扇转速方法:天天观热点
- 天天观速讯丨迷你英雄无限无法连接服务器是什么原因?常见解决方案
- 信息:步步高手机短信铃声怎么设置?步步高手机短信铃声默认响铃为10秒
- 手机定位软件怎么设置?360手机助手教你一招
- seo入门知识有哪些?seo入门需掌握的基础知识
- 显示器色域检测软件怎么下载?显示器色域检测图拉丁版
- 茅台推出酒瓶装冰淇淋酷似飞天茅台:66元一瓶:热资讯
- 【剑灵力士】新版本力士职业天赋加点推荐 备战不删档-焦点
- 酷派7230如何刷机?酷派手机刷机流程详解-当前通讯
- 小天才宝贝电脑市场报价及市场评价推荐
- 《寓言之地》将于4月13日在Steam开启抢先体验测试
- steam手机令牌怎么绑定?steam令牌绑定教程:焦点观察
- 天天微资讯!obee手机怎么刷机?欧比OBEEK8009刷机教程
- 新浪微博怎么加好友?新浪微博加好友的具体操作 今亮点
- 当前观点:小型冰箱价格多少钱?300-400元的小冰箱推荐
- 央企专业化整合精准提速-世界热资讯
- 环球观点:《GT赛车7》今日更新免费追加丰田豪华MPV
- 全球快看点丨《最后的生还者》PC版更新仍未解决卡顿等问题
- 资讯:Curved Lines展览征集稿件
- MINISFORUM NAB6迷你主机上架:i7-12650H准系统低至2599元:天天热议
- 固态PCIe转接卡5.9元
- 松下极简电吹风159元:天天快资讯
- 迪斯尼取缔元宇宙部门
- 怎样去除甲醛最好_怎样去除甲醛最有效
- PC《最后生还者:第一部》4080 4K帧率测试 DLSS效果出色
- 新一代高贵“亮机卡”!RTX 4050被曝6月发布:弱得不像话 动态焦点
- 天天热点评!PS+欧美服4月会免游戏公布 《麻布仔大冒险》等
- 《霍格沃茨之遗》成为2023年2月北美最畅销电子游戏_世界快看点
- Steam明年不再支持Win7、8!
- 接手机宾得望远镜889元_天天报资讯
- 透视上市券商年报:两融业务不乏亮点 仍需多维度补短板:每日热文
- 广宇发展:2022年营收34亿元,净利6.33亿元 | 年报快讯
- 斯坦福大学医院外科医生培训 白男占比0%引争议
- 环球今日报丨TGA红灵入侵少年再出现:送给甲亢哥梅西签名球衣
- 玲娜贝儿设计师遭裁员:已经累计为迪士尼公司工作22年-焦点快报
- 世界视讯!超过90%会员表示:如果没有XGP就不会尝试在玩的游戏
- 《地平线:西之绝境》DLC“燃烧海岸”新截图公开-今日最新
- 每日热点:有学历如何去澳大利亚工作_办理澳大利亚工作签证需要什么条件
- 《塞尔达传说:王国之泪》武器融合系统《蟹蟹寻宝奇遇》早有了:别说我抄袭|世界即时
- 世界热资讯!《TLOUP1》PC版首日峰值36496人 位列PS游戏第四