1. 线程的使用
继承Thread类或者实现Runnable接口
Thread 和Runnable的区别
- 克服java单继承的缺点
- 当多个线程共用同一资源时,用Runnable比较好
因为在创建线程的时候,构造方法中有一个Runnable的参数,多个线程可以共用这一个Runnable
2. 线程中用到的一些关键字和方法
synchronized
互斥,使得共享的资源同一时刻只能由一个线程占用
volatile
保证了线程写入的值能够及时刷新到主内存
线程读取的值总是从主内存中读取的
一些常用方法
start
启动一个线程
join
加入一个线程,加入的线程先执行完毕,在执行其他的线程
stop
错误的终止线程的方法,它会使得线程戛然而止,没办法做一些线程结束的后续操作
interrupt
当线程处于非阻塞状态时,则将中断标志修改为true,在此基础上如果调动sleep,wait,join等阻塞方法时,将会抛出InterruptedException异常,并且将中断标志修改为false。因此最正确的终止线程的方法,应该是通过标志位来判断。
sleep
线程进行睡眠
wait
线程等待,可以设置等待的时间,如果没有设置时间,就需要进行唤醒操作
notify
随机唤醒一条处于等待状态的线程
notifyAll
唤醒所有处于等待状态的线程
yield
让出处理器的时间,使得其他线程可以竞争资源
3. 线程的生命周期
创建 new Thread
就绪 start notify notifyAll
运行 执行run方法
阻塞 sleep wait block(被synchronized锁阻塞)
终止 run方法执行完毕
4. java线程非分类
用户线程
运行在前台,执行具体任务
守护线程
运行在后台,为其他前台线程服务
Thread.setDaemon(true) 可以将线程设置为守护线程
注意,要在start之前调用,在守护线程中创建的线程也是守护线程
用户线程执行完毕后,守护线程自动终止
工具
jstack 查看线程快照
cmd 命令 jstack -l pid 查看进程中 线程的信息
5. java多线程的可见性问题
可见性
一个线程对共享变量值的修改,能够及时地被其他线程所看到
共享变量
一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量
JMM(java内存模型)
描述了java程序中各种变量(线程共享变量)的访问规则,以及在jvm中将变量存储到内存和从内存中读取出变量的底层细节
1.所有变量都存储在主内存中
2.每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
实现共享变量的可见性必须保证以下两点
1.线程修改的共享变量能够及时从工作内存中刷新到主内存中
2.其他线程能够及时将共享变量的值从主内存中刷新到自己的工作内存中去
实现方式
synchronized
线程解锁前,将共享变量的值及时地更新到主内存中去
线程加锁后,将清空工作内存中共享变量的值,并从主内存中读取共享变量的值
(加锁与解锁要是同一把锁)
volatile
重排序
代码书写的顺序与代码实际执行的顺序不同,指令冲排序是编译器或处理器为提高程序性能而做的优化
1.编译器优化冲排序(编译器优化)
2.指令级并行重排序(处理器优化)
3.内存系统的重排序(处理器优化)
as-if-servial
无乱怎么进行重排序,代码的执行结果应该与代码顺序执行的结果保持一致
不可见的原因
1.线程的交叉执行
2.重排序集合线程的交叉执行
3.共享变量未及时更新
synchronized的解决方案
1.原子性
2.可见性(synchronized本身就会保证这一点)
volatile的解决方案
深入来说:通过加入内存屏障和禁止重排序优化来实现
对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
对volatile变量执行读操作时,会在读操作后加入一条load屏障指令
通俗来说:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,当该变量发生变化时,又会强迫线程将该变量的值刷新到主内存中
volatile不能保证原子性
多线程中安全使用volatile变量,必须满足
1.对变量的写入操作,不能依赖当前的值
不满足: 如number++ count*=count等
满足:boolean变量,记录温度变化
2.该变量没有包含在具有其他变量的不变式中
不满足:不变式 low<up
比较synchronized与volatile
执行速度
synchronized会阻塞线程,而volatile不会
安全性
synchronized保证操作的原子性和可见性,而volatile只能保证可见性
注意:共享变量的值定义为private
注意:transient也可以保证可见性,如在集合中的modCount(修改的次数,set不记录)被定义为transient
java并发包
Callable
类似Runnable 执行耗时操作,并可以将计算结果作为返回值,同时还可以抛出异常,Runnable则不能
Future 可以拿到这个返回值(get方法)
用法
new Thread(future).start()
线程的介绍就先到这了,更详细的请戳我的多线程系列
网友评论