一、什么是多线程安全问题
当多个线程共享同一个全局或者静态变量,做写的操作,可能会发生数据冲突问题,也就是线程安全问题。读操作和局部变量是不会发生的。
二、线程安全的解决方法:该资源只能让当前线程操作,操作完成之后,再让其他线程执行
- 多线程之间同步:synchronized
- 使用锁:lock
三、synchronized作用于同步代码块
- 语法:
synchronized(obj){//obj就是同步锁,要想执行下面代码,就要先获得此锁。做个锁是任意的,可以是你想指定的obj,也可以是一个任意obj.
//这里就是同步代码块
}
实例代码
public void sale() {
//同步代码块 synchronized 包裹需要线程安全的问题。
synchronized (oj) {
if(train1Count>0){
System.out.println(Thread.currentThread().getName()+ ",出售第" + (100 - train1Count + 1) + "票");
train1Count--;
}
}
}
四、synchronized作用(非静态)方法
同步方法,被synchronized修饰的方法,这种形式不用显示指定锁,因为this就是锁。比如两个线程同时操作一个对象,那么就要在(该对象对应的类中)需同步的地方加锁
代码实例
public synchronized void sale() {
if (trainCount > 0) {
try {
Thread.sleep(40);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + ",出售 第" + (100 - trainCount + 1) + "张票.");
trainCount--;
}
}
ps:当作用于静态方法的时候,锁为字节码文件,比如A.class
五、什么时候会释放锁
- 跳出加锁区域(正常执行完或者异常)
- 调用同步监听对象的wait方法
六、什么时候加锁
当出现线程安全问题的时候。
七、死锁
当两个线程,互相等待对方释放锁(至少两把锁)时,就会发生死锁。
代码实例:
/**
* 一个简单的死锁类
* 当DeadLock类的对象flag==1时(td1),先锁定o1,睡眠500毫秒
* 而td1在睡眠的时候另一个flag==0的对象(td2)线程启动,先锁定o2,睡眠500毫秒
* td1睡眠结束后需要锁定o2才能继续执行,而此时o2已被td2锁定;
* td2睡眠结束后需要锁定o1才能继续执行,而此时o1已被td1锁定;
* td1、td2相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。
*/
public class DeadLock implements Runnable {
public int flag = 1;
//静态对象是类的所有对象共享的
private static Object o1 = new Object(), o2 = new Object();
@Override
public void run() {
System.out.println("flag=" + flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500); //要有这个,才能够有效果发生
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("1");
}
}
}
if (flag == 0) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
DeadLock td1 = new DeadLock();
DeadLock td2 = new DeadLock();
td1.flag = 1;
td2.flag = 0;
//td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
//td2的run()可能在td1的run()之前运行
new Thread(td1).start();
new Thread(td2).start();
}
}
八、多线程的三大特性:原子性,可见性,有序性
多线程编程要确保并发程序正确地执行,必须要保证原子性、可见性以及有序性,缺一不可,不然就可能导致结果执行不正确。
- 原子性:和事务一样,即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
- 可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值,可以使用volatile关键字修饰变量,就可见。(JMM java内存模型 在工作内存中修改后,里面刷入主内存,让其他线程看见)。ps:Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,粗略的理解,主内存和工作内存就可以了。
- 有序性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。