1.进程和线程的区别
进程:一个程序或服务运行的过程在操作系统中的体现,操作系统中一个独立的程序或服务
线程:一个进程的内部还可以划分出多个并行执行的过程。一个进程内部可以存在多个线程
计算机中只有一个CPU,实际上同一时刻只能处理一个运算,只是CPU处理速度非常快,看起来这些进程和线程都在同时执行。
2.启动线程的三种方式
一。
1.创建一个类,实现Runnable接口,在其中的run()方法中编写启动的线程要执行的代码
2.创建该类的对象,传入Thread的构造方法,创建Thread对象
3.调用Thread对象start()方法,启动线程
二。
1.写一个类继承Thread,覆盖其中的run()方法,在其中编写要启动的线程要执行的代码
2.创建该Thread类的子类对象
3.调用Thread类的子类的对象的start()方法,启动线程
三。
实现Callable接口,重写call方法
3.Callable和Runnable的区别
1.Runnable线程执行完成之后没有返回值;Callable需要定义返回值类型并返回结果
2.Runable线程可以通过Thread启动,也可以通过线程池启动执行;Callable只能通过线程池来执行
3.Runnable没有容错机制,一旦出现异常需要自己来处理(run方法不能抛异常);Callable可以将异常抛出利用全局的方式来进行处理(call方法可以抛出异常)
4.多线程并发安全问题产生的条件
1.有共享资源
2.有多线程并发操作了共享资源
3.有多线程并发操作了共享资源涉及到了修改操作
5.解决多线程并发安全问题
关键是破坏产生多线程并发安全问题的条件
1.禁止共享资源
2.禁止多线程并发操作
3,禁止修改
6.使用多线程的优缺点
优点:
1.多线程技术使程序的响应速度更快
2.当前没有进行处理的任务可以将处理器时间让给其他任务
3.占用大量处理时间的任务可以定期将处理器时间让其他任务
4.可以随时停止任务
5.可以分别设置各个任务的优先级以及优化性能
缺点:
1.等待使用共享资源时造成程序耳朵运行速度慢
2.对线程进程管理要求额外的cpu开销
3.可能出现死锁情况。(较长时间的等待或资源竞争以及死锁等情况)
7.start()和run()方法简介和区别
为什么我们调用start()方法时。会执行run()方法,为什么不能直接调用run()方法:
start():启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码
通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪状态,并没有运行,一旦得到CPU时间片,就开始执行run()方法
run():只是类的一个普通的方法,如果直接调用run()方法,程序的依然只有主线程这一个线程,其程序执行路径还是只有一条。
总结:
1.调用start方法可以启动线程
2.run方法只是thread的一个普通方法调用,还是在主线程里执行
3.把需要并行处理的代码放在run方法中,start方法启动线程将会自动调用run方法,这是由jvm的内存机制规定的
4.run方法必须是public访问权限,返回值为void
8.sleep()方法和wait()方法的相同点和不同点?
相同点:都可以让线程处于冷冻状态
不同点:
1.sleep方法是Thread类定义的方法,而wait方法是Object类中定义的方法
2.sleep方法必须人为的为其设置时间,wait方法即可以指定时间,也可以不指定
3.sleep方法时间到,线程处于临时阻塞状态或者运行状态,wait方法如果没有被设置时间,就必须要通过notify或者notifyAll来唤醒
4.sleep方法不一定非要定义在同步中,wait一定要定义在同步中
当两者都定义在同步中,线程执行到sleep,不会释放锁。线程执行到wait,会释放锁
9.生产者和消费者模型的作用是什么?
1.通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用
2.解耦,这是生产者消费者模型附带的作用,两者联系少,相互制约的较少
10.wait()方法和notify/notifyAll方法在放弃对象监视器时有什么区别?
wait()方法会立即释放对象监视器
notify/notifyAll方法会等待线程剩余代码执行完毕才会放弃对象监视器
11.什么是悲观锁和乐观所
乐观锁和悲观锁是两种解决更新丢失的解决方案
乐观锁:每次查询都不会造成更新丢失,会在每次更新前手动确认一下查询的结果是否依旧有效,可能导致频繁的重试
悲观锁:悲观的认为每次查询的时候,就会造成更新丢失,因此在查询时,会手动个表加上排他锁。代码格式:在查询语句后面加上for update;添加上了排他锁
同一时间,只有一个事务执行查询操作,和后续的更新操作其他事务需要等待该事务执行完毕,
才能去执行查询
结论: 如果查询多,更新少---->使用乐观锁
如果查询少,更新多---->使用悲观锁
12.产生死锁的条件?如何解决死锁?
1.多把锁
2.多个程序
3.同步嵌套
在Synchronized代码块中再包含Synchronized代码块,这就意味着,占用着一部分锁,再要求另一部分锁
避免死锁
避免同步嵌套来避免死锁的产生
检测并打断死锁
有时无法进行避免死锁的操作,此时只能不停的检测是否有死锁产生,如果有死锁产生,则打断死锁,所谓的打断死锁,就是将造成死锁的某一线程错误退出,打断对锁互相要求的环,从而使程序可以正常运行下去。
13.java中的volatile关键字是什么作用?
多线程使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,(每次读取到volatile变量,一定是最新的数据)
java代码执行中,为了获取更好的性能JVM可能会出现一些意想不到的问题,使用volatile则会对禁止语义重排序,但是在一定程度上降低了代码执行效率。
14.wait(),notify(),notifyAll()区别?
wait()
在Synchronized代码块中,调用锁对象的wait方法将会使当前线程进入进入阻塞状态 不再争夺cpu 释放锁 阻塞的状态会一直持续下去 直到被其他线程 调用锁对象的notify()或notifyAll()方法唤醒
notify()
在Synchronized代码块中,调用锁对象的notify()方法,将会唤醒之前在这个锁对象上进入wait()状态的某一个线程,使其退出阻塞状态,退出阻塞状态后,恢复对cpu的争夺,但仍然需要得到锁,才可以继续运行。至于唤醒的是哪一个线程是不确定的
notifyAll()
在Synchronized代码块中,调用锁对象的notifyAll()方法,将会唤醒之前在这个锁对象上进入wait()状态的所有线程,使这些线程退出阻塞状态,退出阻塞状态后,恢复对cpu的争夺,但仍然需要得到锁,才可以继续运行。
15.java线程的状态转化图
16.用户线程和守护线程有什么区别?
任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(boolean);true则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。
唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。所以应该避免在守护线程中读写固有资源等操作如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
17.什么是线程调度器和事件分片?
线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让的程序依赖于线程的优先级)。
18.如何确保main()方法
我们可以使用thread类的joint()方法去报所有程序创建的线程在main()方法退出前结束