一、线程安全问题为什么会存在
1.当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
二、代码例子
1.代码
public class ThreadTrain implements Runnable {
private int trainCount = 100;
@Override
public void run() {
while (trainCount > 0) {
try {
Thread.sleep(500);
} catch (Exception e) {
}
sale();
}
}
public void sale() {
if (trainCount > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
trainCount--;
}
}
public static void main(String[] args) {
ThreadTrain threadTrain = new ThreadTrain();
Thread t1 = new Thread(threadTrain, "①号");
Thread t2 = new Thread(threadTrain, "②号");
t1.start();
t2.start();
}
}
2.结果
3.分析
一号窗口和二号窗口同时出售火车票,部分火车票会重复出售,结论发现,多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题。
三、线程安全的解决方法
1.使用多线程之间同步synchronized或使用锁(lock),将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
2.内置锁使用synchronized关键字实现,synchronized关键字有两种用法:
2.1.修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象
2.2.同步代码块和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活。
2.3.每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁,内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁。
3.代码(同步方法,其他的方法省略)
public class ThreadTrain implements Runnable {
private int trainCount = 100;
@Override
public void run() {
while (trainCount > 0) {
try {
Thread.sleep(500);
} catch (Exception e) {
}
sale();
}
}
public synchronized void sale() {
if (trainCount > 0) {
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
trainCount--;
}
}
public static void main(String[] args) {
ThreadTrain threadTrain = new ThreadTrain();
Thread t1 = new Thread(threadTrain, "①号");
Thread t2 = new Thread(threadTrain, "②号");
t1.start();
t2.start();
}
}
3.1.结果(结果正常)
①号,出售第1张票
②号,出售第2张票
①号,出售第3张票
②号,出售第4张票
①号,出售第5张票
②号,出售第6张票
①号,出售第7张票
四、ThreadLocal的用法
1.ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量(每个线程都有一个)。
2.当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
3.代码
class Res {
// 生成序列号共享变量
public static Integer count = 0;
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
protected Integer initialValue() {
return 0;
};
};
public Integer getNum() {
int count = threadLocal.get() + 1;
threadLocal.set(count);
return count;
}
}
public class ThreadLocaDemo2 extends Thread {
private Res res;
public ThreadLocaDemo2(Res res) {
this.res = res;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + "---" + "i---" + i + "--num:" + res.getNum());
}
}
public static void main(String[] args) {
Res res = new Res();
ThreadLocaDemo2 threadLocaDemo1 = new ThreadLocaDemo2(res);
ThreadLocaDemo2 threadLocaDemo2 = new ThreadLocaDemo2(res);
ThreadLocaDemo2 threadLocaDemo3 = new ThreadLocaDemo2(res);
threadLocaDemo1.start();
threadLocaDemo2.start();
threadLocaDemo3.start();
}
}
4.结果
Thread-0---i---0--num:1
Thread-0---i---1--num:2
Thread-0---i---2--num:3
Thread-2---i---0--num:1
Thread-2---i---1--num:2
Thread-2---i---2--num:3
Thread-1---i---0--num:1
Thread-1---i---1--num:2
Thread-1---i---2--num:3
五、结束
1.今天这一个就说到这里吧!!!