java-线程学习总结
1.程序、进程、线程:
程序:用某种语言编写完成一些特定任务的指令集合。
进程:程序的执行过程、或正在执行的程序。
线程:程序内部的一条执行路径,一个进程有多个线程。
2.线程的调度:
1)优先级高的优先调度抢占式策略。(但不是高优先级一定先执行,只是概率更高。)
2)同优先级根据先到先服务,时间片策略。
3.线程的创建:
1)继承thread类:重写run(),调用start()。
2)实现Runnable接口方式
注:thread与runnable优先选择runnable。
原因一:thread类有单继承限制。
原因二:runnable接口可以适合共享数据,因为new两个thread,会有两个对象来调用run方法,而runnable始终是一个。
3).实现callable,更强大:可以返回值、可以抛异常。需要借助FutrueTask
4).使用线程池
4.1).适用情况:经常创建和销毁线程、并发情况等
4.2).思路:提前创建好多线程,放入线程池(如数据库连接池),使用时,直接获取,避免重复创建和销毁,重复使用,提高响应速度。
4.3).线程管理:线程池最大最小值等其他属性
4.4).java线程池相关API:ExecutorService(真正的线程池接口,常见子类ThreadPoolExecutor)和Executors
4.5).springboot:提供@Async、@EnableAsync、配置好线程池,就可以异步执行线程。(注意:异步注解类方法,不能和调用异步方法写在同一个类下,否则不生效。)
4.线程通信方法:
wait()[进入锁住的区域,释放锁等待,让别的线程先执行]/notify()唤醒wait的线程。
注:wait,notify用在有锁的地方,如:synchronize修饰的同步方法和同步代码
5.线程的分类:
1).守护线程:eg:垃圾回收线程、异常处理线程
2).用户线程
6.线程的生命周期:
新建——就绪——运行——阻塞——死亡
新建,调用start()到就绪
就绪获取cpu执行权,若调用yield()返回就绪状态
运行时,调用sleep()、join()、等待同步锁、wait()、suspend(),到阻塞状态
阻塞时,调用sleep()时间到、join()结束、获取同步锁、notify()或者notifyAll()、resume() 到就绪状态
执行完run().调用stop(),死亡。
sleep与wait比较:
相同点:一旦执行方法以后,都会使得当前的进程进入阻塞状态
不同点:
1.两个方法声明的位置不同,Thread类中声明sleep,Object类中声明wait。
2.调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块或者同步方法中
3.关于是否释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放,wait会释放
7.线程同步与线程安全:
1).线程安全:多个线程对同一个共享数据进行操作时,线程没来得及更新共享数据,从而导致另外线程没得到最新的数据,从而产生线程安全问题。
2).解决方法:
a.同步代码块:使用同步监视器(锁)修饰代码块
Synchronized(同步监视器){ //需要被同步的代码 }
b.同步方法:使用同步方法,对方法进行synchronized关键字修饰
注1.runnable接口,直接用synchronized方法、对于继承Thread类,需要用static和synchronized同时修饰,因为对象不唯一
注2.非静态的同步方法,同步监视器是this、静态的同步方法,同步监视器是当前类本身。继承自Thread
3).使用jdk中ReentrantLock中的lock方法与unlock方法。
注1:相同点:synchronized和lock都可以解决线程安全问题
注2:不同点:synchronized机制在执行完相应的代码逻辑以后,自动的释放同步监视器,lock需要手动上锁与释放锁。
8.线程的死锁:
8.1.死锁产生的原因:线程1,持有L1锁申请L2锁,线程2,持有L2锁申请L1锁,造成死锁。
8.2.解决办法:
a.减少同步共享变量
b.通过算法,规定多个线程先后执行的顺序
c.减少锁的嵌套
参考链接: 多线程:https://blog.csdn.net/weixin_44797490/article/details/91006241;
参考链接: 死锁:https://www.cnblogs.com/neon/p/10937080.html;
浙公网安备 33010602011771号