线程安全
如果多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
线程同步
当我们使用多个线程访问同一资源的时候,且多个线程对资源有写的操作,就容易出现线程安全问题。
要解决上述多线程并发访问一个资源的安全性问题,Java中提供了同步机制synchronized来解决。
有三种方式来完成同步操作:
- 同步代码块
- 同步方法
- 锁机制
同步代码块
同步代码块:synchronized关键字可以用于方法的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(同步锁){
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁
- 锁对象可以是任意类型
- 多个线程对象要使用同一把锁
注意:在任何时候最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外面等着。
使用同步代码块解决线程安全问题
public class Ticket implements Runnable{
private int ticket=100;
Object lock=new Object();
public void run(){
while(true){
synchronized(lock){
if (ticket>0){
try {
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
String name=Thread.currentThread().getName();
System.out.println(name+"正在卖:"+ticket--);
}
}
}
}
}
同步方法
同步方法:使用synchronized修饰的方法,就叫作同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁
对于非静态方法,同步锁就是this
对于静态方法,我们 使用当前方法所在类的字节码对象(类名.class)
线程状态
当线程被创建并启动后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期一共有6中状态
New(新建) | 线程刚被创建,但是并未启动。还没调用start方法 |
---|---|
Runnable(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己的代码,也可能没有,这取决于操作系统处理器 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态 |
Waiting(无限等待) | 一个线程在等待另一个线程执行一个唤醒动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒 |
Timed Waiting(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这个状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep,Object.wait |
Teminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡 |
sleep方法的几个注意点
- 进入TIMED_WAITING状态的一种常见情形是调用sleep方法,单独的线程也可以调用,不一定非要有协作关系
- 为了让其他线程有机会执行,可以将Thread.sleep()的调用放在线程run()之内,这样才能保证该线程执行过程中会睡眠
- sleep与锁无关 线程睡眠到期自动苏醒,并返回到Runnable状态