由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问机制上的冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制(synchronized),当一个线程对象获得对象的排他锁,独占资源,其他线程必须等待,使用完后释放锁即可,存在以下问题
- 一个线程持有锁会导致其他所有需要此锁的线程挂起(比如我们上测试加锁,别人在外面等着,外面等待的人肯定不舒服,但是没办法,要保证安全就要失去性能,要保证性能就要失去安全)
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引起性能问题(比如一个想小便的人(优先级高)被一个想大便的人抢了厕所(优先级低),他需要小便一分钟但是需要等待别大便人10分钟)
什么是同步?
同步就是在不出问题的前提下一个一个来
一般发生在多个线程操作同一个资源,它有一个专业的词汇叫:并发
什么是并发,如何解释,它有什么作用
并发:同一个对象被多个线程同时操作
处理多线程问题时,多个线程访问同一个对象,并且某个线程还想修改这个对象,这时候我们就需要用到线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程才能再使用
并发这种机制可以保证我们的数据安全并且大幅度提高效率
队列和锁
队列()
现实生活中,我们都会遇到同一个资源,多个人都想使用的问题,比如食堂排队打饭,每个人都想吃饭,最天然的解决办法就是排队一个一个来,但是在我们开发中,我们也有个专业名词叫队列
锁(lock)
锁就是保证我们并发安全性的工具,比如只有一个厕所,但是好几个人用,我们上厕所关上门就相当于上锁,解决完之后,再释放锁让其他线程使用
它们都是为了保证我们并发的安全性
线程同步形成的条件:队列+锁
守护线程(daemon)
- 有一个方法setDaemon(Boolean xxx),参数为布尔类型,为真守护,为假不守,默认为false,表示是用户线程,正常的线程都是用户线程,除非加了setDaemon(Boolean xxx)才是守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕(main就是一个用户线程)
- 虚拟机不用等待守护线程执行完毕(gc垃圾回收就是一个守护线程,后台记录操作日志,监控内存,垃圾回收等待都是守护线程的例子)
package com.wyh.thread; /** * @program: Thread * @description: 测试线程守护 * @author: 魏一鹤 * @createDate: 2022-01-06 22:03 **/ //可以想象例子 上帝守护人类 在 // 这个例子中 上帝是永生的 人类是暂时的 // 上帝相当于守护线程,人类是一个用户线程,上帝守护者我们 public class TestThreadDaemon { public static void main(String[] args){ //用户 Person person = new Person(); Thread threadPerson = new Thread(person); //守护 God god = new God(); Thread threadGod = new Thread(god); //设置守护 默认为false,表示是用户线程 //正常的线程都是用户线程,除非加了setDaemon(Boolean xxx)才是守护线程 threadGod.setDaemon(true); //上帝守护线程启动 threadGod.start(); //人类线程启动 threadPerson.start(); } } //上帝 class God implements Runnable { @Override public void run() { while (true) { System.out.println("上帝守护者人类"); } } } //人类 class Person implements Runnable{ @Override public void run() { for (int i = 0; i < 365; i++) { System.out.println("人类开开心心的活着"+i+"天"); } System.out.println("=======goodbye======"); } }