【狂神说Java】多线程详解

作者: 遇见狂神说分类: 计算机技术 发布时间: 2019-05-31 20:33:18 浏览:1684750 次

【狂神说Java】多线程详解

一只不萌呀:
//补充被剪辑掉的部分代码 public synchronized void push(Product product) { if (count == 10) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } products【count】 = product; count++; this.notifyAll(); } public synchronized Product pop() { if (count == 0) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } count--; Product product = products【count】; this.notifyAll(); return product; }

【回复】补充一点, 判断count ==10或0的那一句,最好不要用if,应该用while,否则当有多个消费者的时候,会出现脏判断的
【回复】回复 @咸鱼翻身-- :if语句中醒来的线程 不会再一次进行判断了 而while会重新再判断
【回复】回复 @MHz浮夸撒啊 :println应该放在pop和push方法体里面,因为可能正要打印的时候,线程切换了,要保证增改操作和打印是一气呵成的(原子操作)
NULL错误:
他这里说的锁银行而不是锁this 指的是当前类 也就是银行的class Drawing.class 而synchronized方法 想让锁对象是当前类的class ,要么只能有一个银行的实例对象, 要么ynchronized方法前用static修饰 这样也能保证锁对象是类的class 而他有两个银行的实例对象 you 和girlfriend 两个实例对象 那么这两个实例对象开启了两条线程,每条线程用的锁对象都是当前实例对象 锁对象不同是无法实现同步的。 最简单的方法就是 用synchronized代码块 在synchronized(){}, 小括号中放当前类的class,因为一个类是只能有一个class的 所以 能保证锁对象是一样的 而实现同步 ps:小括号中只要是一个唯一的就行 对象 class都可以 其实只要记住下面这三点 任何时候都不会搞错同步方法或同步代码块中的锁对象是什么了 对于普通同步方法,锁是当前实例对象。 如果有多个实例 那么锁对象必然不同无法实现同步。 对于静态同步方法,锁是当前类的Class对象。有多个实例 但是锁对象是相同的 可以完成同步。 对于同步方法块,锁是Synchonized括号里配置的对象。对象最好是只有一个的 如当前类的 class 是只有一个的 锁对象相同 也能实现同步。

【回复】class怎么锁?应该是Account类的对象account吧..
【回复】回复 @唐古灿 :类锁,每一个类都对应一个class对象,你说的就是 Account.class
【回复】回复 @rkine :十分感谢,搞清楚了
牧00殇:
我是真的不懂弹幕老哥老师纠结读音干嘛?首先这是一个技术类教学视频,不是英语教学视频;其次,你要纠正读音,你得可以跟狂神商量一下,你来个二次创作,把视频重置一下,把读音全部都纠正过来。假设一个场景,你老师在上面讲课讲到一个知识点,提了一嘴 模(mo)样,你又站出来了,老师,是模(mu)样。这样是没错,但是你不觉得很尬嘛?就非得享受一下众人皆醉,我独醒的态度嘛?又不是讲错了什么知识点,讲错了,你科普没问题,只是不同口音读音而已。

【回复】这都是闲的,真正学的谁开弹幕,开弹幕也只是不想错过小伙伴说的知识点。。。
【回复】认真的都在学技术,就一群菜鸟叫的比谁都欢。学技术它没这能力,也就只能扯英语找找存在感。
有頭騎士:
lambda在实际开发中用处还是蛮大的,我第一次用lambda是工作中遇到一个难题,需要对数据库查询出的一堆数据先根据班级来排序,班级相同再根据成绩排序,而且还要过滤某些特殊的数据,琢磨半天都觉得不好写,最后就是同事推荐我然后用stream流结合lambda完成的。一般使用都是stream结合lambda替代for循环,可以节省很多代码

【回复】回复 @小AOvo :已经过去几个月了 我现在经常会遇到这种需求 假设我们公司有10种产品 前端给定一个时间区间 我需要查出这段时间内每种产品每天的使用数据 假设某个产品某天没有使用 表内是没有数据的 但是我仍然需要返回那天的数据为0 最后每种产品分别按照使用次数倒叙排序 这样就是10个list 放进一个map返回 一般这种比较复杂的逻辑我喜欢在代码层写而不是写个sql 就算写出来了也可能是慢sql 毕竟数据库的资源很宝贵 不能因为一个访问量大数据多的接口把数据库搞挂掉 代码层用lambda grouping by+sort就很容易解决 这是一个比较简单的例子 还有很多复杂需求很难打字说清楚 总之还是看需求来选择sql还是逻辑代码吧
【回复】这不是可以直接写sql的时候就进行排序吗
【回复】回复 @小AOvo :盲生,你发现了华点,笑死我了
Kge男友:
有个讲死锁的弹幕有点好笑,面试官:给我讲一下什么是死锁。我:你先给我发offer。

【回复】你不讲清楚我怎么给你发 你不发我怎么给你讲[doge]
【回复】这还只是两个对象两把锁的情况,还有环路死等[doge]
skyLetme:
越学习越发现自己越无知,大三上学期开始学习java语言,期末考试90+,老师讲的和书本上的知识自认为还是掌握挺扎实,后来开始学习企业框架,到后面分布式,到现在大四有回来复习多线程,才发现最难的不是技术框架或者开发思想,最难的反而是java基础。计算机底层原理,算法思维,数据结构,这些才是衡量一位优秀程序员的标准。

【回复】兄弟我和你一摸一样,框架学完,才发现这些不是重点,现在也是大四,秋招是赶不上了,只能春招了
【回复】回复 @爪洼ing :只会基础不会框架,面试有机会过;只会框架不会基础,面试妥妥挂
【回复】回复 @爪洼ing :[doge]我学完基础发现要学ssm,js,jQuery,ssm学完发现还有springboot,学完springboot发现还要学cloud,security,Redis,dubbo,vue。
Wendy游戏:
//产品 class Chicken{ int id; public Chicken(int id){ this.id=id; } } //缓冲区 class SynContainer{ Chicken 【】 chickens=new Chicken【10】; int count=0; public synchronized void push(Chicken chicken) { while (count == chickens.length) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } chickens【count】 = chicken; count++; this.notifyAll(); } public synchronized Chicken pop() { while (count == 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; Chicken chicken = chickens【count】; this.notifyAll(); return chicken; } }

【回复】为什么我的鸡能吃到 80多 90多。。。不能控制在10左右 哪个大佬帮我解释一下[热]
【回复】回复 @你猜我猜你猜不猜哒 : 我的也是,生产到100只鸡了,我觉得是for循环的原因,因为run方法里的for循环是定的100,但是容器规定的是10,我这里虽然生产到100只鸡了,但是每次也只是10只生产一次
八月飞白草:
对于数组下标越界问题的解释: 1、狂神的这个例子只适用于一个生产者一个消费者的情况,因为消费者wait()释放对象锁权限让出CPU只会是那唯一的生产者重新拿到锁权限进行生产,这样的话不消费就生产,不生产就消费,是不会出现下标越界的,而如果不止一个消费者就会有下标越界的问题,原因如2; 2、首先要说明,wait()表示持有对象锁的线程准备释放对象锁权限和让出 cpu 资源并进入等待状态。如果有多个消费者,比如X1,X2,X3,假如此时X1,X2,X3都处于wait状态,这时容量为0了生产者拿到锁,生产者生产了1个资源让出锁,X1拿到锁消费完之后容量又刚好为0,然后X1释放锁notifyAll通知JVM去唤醒所有竞争Container对象锁的线程,如果这个锁被X2拿到,那么就会导致0--出现数组下标越界的问题,解决方案暂时只想到把消费的if(index <=0)换成while就是让消费者线程被唤醒的时候不要立刻执行下面的代码,而是再去判断当前容量。 while (index <= 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }

【回复】回复 @缘之哀 :在多线程中要测试某个条件的变化时(尤其是用于线程通信的条件判断)不要选择if,而是选择while去判断。因为notify唤醒沉睡的线程后,线程会接着上次的执行继续往下执行(需要注意的是,执行wait()的线程被notify唤醒的时候,只是让while循环继续往下走,如果用if的话,继续往下走意味着跳出if语句块),所以必须使用while循环阻塞。在本例中,如果管道中有一只鸡,那么第一个消费者被唤醒去消费,消费了一只鸡之后,释放消费的锁,因为唤醒使用的是notifyAll,所以是唤醒所有等待的线程再去由CPU决定把锁给谁,如果此时不是生产者拿到了锁而是第二个消费者拿到锁,那么使用if的话则不会再做循环判断,会继续消费,从而造成数组下标越界。(小白理解,仅供参考)
【回复】回复 @张pot粉我 :意思是消费者1释放锁后消费者2 拿到锁有可能不会判断if(index <=0)直接去拿,while (index <= 0)就保证会再次判断不会直接拿吗?请问原理是?我猜测是if是判断语句,while是循环语句的原因?
【回复】如果是多个生产者多个消费者的话,就是把push和pop里面的if都换成while
dazhong3:
我觉得多线程最大的问题是不知道什么时候能试,学了也不知道什么时候用。

【回复】多线程有用的。我让你思考一个场景。 1.比如 你用tcp来接受客户端发送的监控器传来的图像,对面一直发的哦。 因为是实时显示。 2.然后把接到的图像用画笔在本地显示出来。 如果你单线程写保证把你卡死。
【回复】对,我上班一年都没用到过,问同事也都是没有遇到过多线程[微笑]
【回复】终于遇到了,别人拿多线程调我接口,遇到了大事务行锁死锁的问题,高版本mybatis拼sql线程不安全,Redis锁超时导致大批业务失败…
好想成为五级大佬:
对于第20节同步方法及同步块部分,很多人在哪个list10000的例子最后不加sleep不会出现正常的结果,我的理解是,在这个list的例子里,存在两个问题,第一是有些线程执行的太慢了,main线程执行完了,还有部分线程没执行完,第二是是可能会好多线程操作list的同一个l位置。加上synchronized (list)之后解决了第二个问题。第一个问题的原因,是main线程执行完了之后,还会有部分线程没执行完,在最后加上sleep,使得main线程先停止下,等待其他的线程执行完,然后输出就能正常了。

【回复】public class UnsafeList { public static void main(String【】 args) throws InterruptedException { List<String> list=new ArrayList<String>(); Thread【】 t=new Thread【10000】; for (int i = 0; i <10000 ; i++) { t【i】=new Thread(()->{ synchronized (list) { list.add(Thread.currentThread().getName()); } }); t【i】.start(); } //Thread.sleep(100); System.out.println(list.size()+" "+t【9999】.getState()); } } 运行这些代码就可以验证楼主的第一个理解,当最后打印的时候,第9999个线程还处于RUNNABLE状态
【回复】对于这两个例子用CountDownLatch配合synchronized 才可以正常得出结果
【回复】视频中写的两个例子都是这样只要不加sleep延时。结果就不对,真正的项目中谁会这样写,这不是误导吗
花馨:
这声音???你真是以前玩球球那个狂神???

坠下的星河:
public synchronized Chicken push(Chicken chicken){ //如果容器满了,就需要等待消费者消费 while(count == chickens.length){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果没有满,我们就需要丢入产品 chickens【count】 = chicken; count++; //可以通知消费者消费了! this.notifyAll(); return chicken; } //消费者消费产品 public synchronized Chicken pop() { //判断是否能消费 while (count == 0) { //不能消费时(实际数量为0) 消费者等待 生产 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果可以消费 count--; Chicken chicken = chickens【count】; //吃完了通知生产 this.notifyAll(); return chicken; }

【回复】补充说明一点,视频中push和pop方法用的是if,单个消费者的时候没问题,多消费者就会数组越界,因为多消费者的时候用if会造成虚假唤醒,改用while就可以解决问题
给良辰个薄面:
//线程不安全的集合,为什么把睡眠时间写在了进程外面?究竟睡眠的是哪个进程 List<String> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { /*new Thread(()->{ list.add(Thread.currentThread().getName()); }).start();*/ new Thread(new Runnable() { @Override public void run() { list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(5);//写在进程外面有何含义? } catch (InterruptedException e) { e.printStackTrace(); }

【回复】回复 @给良辰个薄面 :想了半天总算明白了:线程也是有顺序的,而主线程在其他线程之前被执行了,导致其他线程还未占好座位,就已经被打印出来了(被统计出来了),加睡眠就是让打印语句,不要那么快被执行,等大家都站好位置后再去统计。所以就可以解释为什么加上睡眠后集合元素会增加了。
【回复】10000个线程交替进行将自己线程名字放入集合,放完之后睡眠5秒有何意义?释放CPU?
【回复】睡的主线程,不睡你会发现即使加了锁,也会出现打印不是10000的现象。加个睡眠尽可能保证先执行那10000个线程,然后再执行打印长度的主线程

JAVA 编程 西部开源Java 多线程 Java Java教程 Java教学 西部开源 多线程并发 狂神说Java

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!