进程与线程
首先,写好的程序是死的,跑起来才叫进程,进程下可以拥有多个线程,真正干活的就是这些线程
多CPU即多核
三种创建方式
创建线程方式一:继承Thread类,重写run()方法,调用start()方法开启线程
注意:线程开启不一定立即执行,由CPU调度执行
如果线程调用的是run方法,那么还是普通的单线程
一般推荐使用Runnable接口,因为java是单继承,为了灵活,所以使用接口
这样可以实现三个线程跑一个对象
静态代理
lambda表达式
简化过程:
1、定义一个类,实现函数式接口
2、将类定义成静态内部类
3、变为局部内部类
4、变为匿名内部类
5、进一步简化为lambda表达式,只保留了参数列表和方法体
线程的状态
五个状态:new创建线程对象,调用start方法进入就绪状态,等待CPU调度,CPU调度进入运行状态,
运行中调用了wait、sleep、或同步锁就进入阻塞状态,阻塞解除后进入就绪继续等待CPU调度
线程中断或者结束,死亡状态,一旦进入死亡状态,线程就不会再次启动了。也就是线程只能启动一次
停止线程
对于抢票问题,模拟网络延时可以发现线程不安全问题
sleep还可以模拟倒计时,记录当前系统时间
礼让就是当前A线程退出去,变为和其他线程一样的就绪状态,但是礼让不一定成功,可能CPU再次调度的时候执行的还是A线程
join就是插队,当这种线程出现时,就会优先执行,插队线程执行完毕后,其他线程才会接着执行
join可能会导致线程阻塞
线程状态观测:thread.getState();
线程优先级设置
优先级的设置只是说CPU在调度的时候调度的概率会发生改变,并不是说一定要先去调度那个优先级高的
守护线程
线程同步
多个线程访问同一个对象,这时就出现了并发问题
每个对象都有一把锁,这个锁就是为了线程同步,一个对象来了以后上锁,解决事情后才打开锁,进入下一个对象
保证安全性
队列 + 锁 才能保证线程的安全性
三个人同时看到一张票,三个人都会把这张票拿到自己的工作内存去操作,三个人都去拿票,这时就导致了出现票数为负数,也就是线程不安全
同步方法
线程不安全主要是因为多个线程同时操作一个对象,而且要对对象的数据进行修改。
所以要控制这些数据对象是线程安全的。
而通过private关键字来修饰数据对象,就可以保证只能是方法可以访问数据对象
所以我们只需要控制方法实现线程安全,也就是对数据对象操作安全了
同步方法会把整个方法都锁上,而我们只是需要锁上那些需要修改的数据对象,所以这会导致锁的资源太多,浪费资源
这时就需要同步代码块
同步方法锁的是当前对象this, 而同步块可以设置需要上锁的对象,这个对象叫做同步监视器
同步块要锁的是需要发生增删改操作的对象,查的话数据没有发生改变,所以不需要同步锁
所以同步块的锁对象是需要根据实际情况而定的
对于抢票,只要在卖票的方法上加一个synchronized关键字即可
对于银行取钱,如果在run方法上加synchronized关键字,是没有用的,因为synchronized默认所的是当前对象,也就是银行这个对象
而这里我是账户发生了改变,所以锁住银行对象无效
必须是所住account对象,才会线程安全,也就是使用同步块
对于线程不安全集合list, 因为发生数据改变的就是list对象,所以发生改变时锁住list即可