Java多线程复习1

1.如何预防死锁?

         破坏死锁的产生的必要条件即可:

        1.首先需要将死锁发生的是个必要条件讲出来

                1.互斥条件:同一时间只能有一个线程获取资源。
                2.不可剥夺条件:一个线程已经占有的资源,在释放之前不会被其它线程抢占

                3.请求和保持条件:线程等待过程中不会释放已占有的资源
                4.循环等待条件:多个线程互相等待对方释放资源


        2.死锁预防,那么就是需要破坏这四个必要条件

                1.由于资源互斥是资源使用的固有特性,无法改变,我们不讨论

                ⒉破坏不可剥夺条件
                        (1.一个进程不能获得所需要的全部资源时便处于等待状态,等待期间他占有的资源将被隐式的释放重新加入到系统的资源列表中,可以被其他的进程使用,而等待的进程只有重新获得自己原有的资源以及新申请的资源才可以重新启动,执行

                3.破坏请求与保持条件
                        (1.第一种方法静态分配即每个进程在开始执行时就申请他所需要的全部资源,

                        (2.第二种是动态分配即每个进程在申请所需要的资源时他本身不占用系统资源

                4.破坏循环等待条件
                        (1.采用资源有序分配其基本思想是将系统中的所有资源顺序编号,将紧缺的,稀少的采用较大的编号,在申请资源时必须按照编号的顺序进行,一个进程只有获得较小编号的进程才能申请较大编号的进程。

2.多线程有哪几种创建方式?


1.实现Runnable,Runnable规定的方法是run(),无返回值,无法抛出异常
2.实现Callable,Callable规定的方法是call(),任务执行后有返回值,可以抛出异常
3.继承Thread类创建多线程:继承java.lang.Thread类,重写Thread类的run()方法,在run()方法中实现运行在线程上的代码,调用start()方法开启线程。

        Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法
4.通过线程池创建线程.线程和数据库连接这些资源都是非常宝贵的资源。那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。

3.描述一下线程安全活跃态问题,竞态条件

        1.线程安全的活跃性问题可以分为死锁、活锁、饥饿
        活锁就是有时线程虽然没有发生阻塞,但是仍然会存在执行不下去的情况,活锁不会阻塞线程,线程会一直重复执行某个相同的操作,并且一直失败重试

        2.饥饿就是线程因无法访问所需资源而无法执行下去的情况,
                饥饿分为两种情况:
                        1.一种是其他的线程在临界区做了无限循环或无限制等待资源的操作,让其他的线程一直不能拿到锁进入临界区,对其他线程来说,就进入了饥饿状态
                        2.另一种是因为线程优先级不合理的分配,导致部分线程始终无法获取到CPU资源而一直无法执行

                 解决饥饿的问题有几种方案:
                        1.保证资源充足,很多场景下,资源的稀缺性无法解决
                        2.公平分配资源,在并发编程里使用公平锁,例如FIFO策略,线程等待是有顺序的,排在等待队列前面的线程会优先获得资源

                        3.避免持有锁的线程长时间执行,很多场景下,持有锁的线程的执行时间也很难
缩短

 

4..Java中的wait和sleep的区别联系?

1.       所属类:首先,这两个方法来自不同的类分别是Thread和Object , wait是Object的方法,sleep是Thread的方法

        sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。但在sleep的过程中过程中有回叹员对象调用它的interrupt().产生InterruptedException异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码

2.作用范围: sleep方法没有释放锁,只是休眠,而wait释放了锁,使得其他线程可以使用同步控制块或方法

3.使用范围: wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用
而sleep可以在任何地方使用


5.描述一下进程与线程区别?

1.进程(Process)
        是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。总结:j进程是指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位
2.线程
        操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。总结:系统分配处理器时间资源的基本单元,或者说进程内独立执行的一个单元执行流。线程——程序执行的最小单位


6.描述一下Java线程的生命周期?

运行,阻塞,销毁,往下推理

1.新建 就是刚使用new方法,new出来的线程;
⒉就绪 就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
3.运行 当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
4.阻塞 在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll)方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
5.销毁 如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源


7.程序开多少线程合适?
这里需要区别下应用是什么样的程序
        CPU密集型程序:一个完整请求,I/O操作可以在很短时间内完成,CPU还有很多运算要处理,也就是说CPU计算的比例占很大一部分,线程等待时间接近0
1.单核CPU:一个完整请求,I/O操作可以在很短时间内完成,CPU还有很多运算要处理,也就是说CPU计算的比例占很大一部分,线程等待时间接近0。单核CPU处理CPU密集型程序,这种情况并不太适合使用多线程,
2多核:如果是多核CPU处理CPU密集型程序,我们完全可以最大化的利用CPU核心数,应用并发编程来提高效率。CPU密集型程序的最佳线程数就是:因此对于CPU密集型来说,理论上线程数量=CPU核数(逻辑),但是实际上,数量一般会设置为CPU核数(逻辑)+1(经验值)
计算(CPU)密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外"的线程,可以确保在这种情况下CPU周期不会中断工作

         

        I/O密集型程序,与CPU密集型程序相对,一个完整请求,CPU运算操作完成之后还有很多I/O操作要做,也就是说V/O操作占比很大部分,等待时间较长,线程等待时间所占比例越高,需要越多线程;线程CPU时间所占比例越高,需要越少线程

1.I/O密集型程序的最佳线程数就是:最佳线程数=CPU核心数(1/CPU利用率=CPU核心数(1+(I/O耗时/CPU耗时))
2.如果几乎全是I/O耗时,那么CPU耗时就无限趋近于0,所以纯理论你就可以说是2N (N=CPU核数),当然也有说2N+1的,1应该是backup
3.一般我们说2N+1就即可
 

8.描述一下notify和notifyAll区别?

        首先最好说一下锁池和等待池的概念

1.锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的
synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中。
2.等待池:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.

1.如果线程调用了对象的wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁
 
2.当有线程调用了对象的notifyAll()方法(唤醒所有wait线程)或notify()方法(只随机唤醒一个wait线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争

3.所谓唤醒线程,另一种解释可以说是将线程由等待池移动到锁池,notifyAll调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。而notify只会唤醒一个线程。

 

9.描述一下synchronized和lock区别?


 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值