1.什么是线程安全问题?
答:当多个线程共享同一个全局变量,做写的时候,可能会受到其他线程的干扰,导致数据有问题,这种线象叫线程安全问题。
有一种情况可以不用讨论:做读的时候,不会产生线程安全问题
还用多个线程共享一个局部变量时,不会发生线程安全问题
案例:
现在有100张火车票,2个窗口同时抢火车票,用多线程模拟
package com.emple2;
/**
* @author shkstart
* @date 2019/6/5- 17:30
*/
class ThreadTrain1 extends Thread{
//总共有100张票
private int trainCount = 100;
@Override
public void run(){
while (trainCount > 0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//出售火车票
sale();
}
}
public void sale(){
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"票");
trainCount--;
}
}
public class ThreadDemo {
public static void main(String[] args) {
ThreadTrain1 threadTrain1 = new ThreadTrain1();
Thread t1 = new Thread(threadTrain1,"窗口1");
Thread t2 = new Thread(threadTrain1,"窗口2");
t1.start();
t2.start();
}
}
出现第101张票的原因:
因为2个线程同时进入sale方法,当第一个线程执行完后trainCount等于0,第二个线程再执行会出现101张票。
解决办法:将trainCount变为局部变量
package com.emple2;
/**
* @author shkstart
* @date 2019/6/5- 17:30
*/
class ThreadTrain1 extends Thread{
//总共有100张票
@Override
public void run(){
int trainCount = 100;
while (trainCount > 0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//出售火车票
//sale();
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"票");
trainCount--;
}
}
/*public void sale(){
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"票");
trainCount--;
}*/
}
public class ThreadDemo {
public static void main(String[] args) {
ThreadTrain1 threadTrain1 = new ThreadTrain1();
Thread t1 = new Thread(threadTrain1,"窗口1");
Thread t2 = new Thread(threadTrain1,"窗口2");
t1.start();
t2.start();
}
}
再次执行不会发生线程安全问题
线程之间的同步:是保证数据的原子性
这个数据不能收到其他线程的干扰
解决办法:
synchronized
lock --- jdk1.5并发包 手动锁
2.synchronized的用法
在多线程共享成员变量时加锁
走到 synchronized (oj) 时,看谁先拿到对象锁,没拿到的只能等待别的线程释放对象锁后才能进行,这样做的好处是这块代码永远只能有一个线程操作,保证了线程的原子性。
代码:
package com.emple2;
import sun.awt.Mutex;
/**
* @author shkstart
* @date 2019/6/5- 17:30
*/
class ThreadTrain1 extends Thread{
//总共有100张票
private int trainCount = 100;
//对象锁
private Object oj = new Object();
@Override
public void run(){
while (trainCount > 0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//出售火车票
sale();
}
}
public void sale(){
//同步代码块,包裹需要线程安全的问题
synchronized (oj){
if(trainCount > 0){
System.out.println(Thread.currentThread().getName()+",出售第"+(100-trainCount+1)+"票");
trainCount--;
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
ThreadTrain1 threadTrain1 = new ThreadTrain1();
Thread t1 = new Thread(threadTrain1,"窗口1");
Thread t2 = new Thread(threadTrain1,"窗口2");
t1.start();
t2.start();
}
}
运行效果:
注意使用到synchronized的时候有2个条件
1.必须有2个以上的线程,需要发生同步
2.多个线程想同步时,必须使同一把锁。
3.保证只有一个线程进行执行
同步的原理:
1.拿到锁,其他线程已经有cpu执行权,一直排队,等待其他线程释放锁。
2.锁在什么时候释放?在代码执行完毕或者是程序抛出异常都会被释放
3.锁已经被释放掉的话,其他线程开始获取锁进去同步中去
4.锁的资源竞争。
缺点:效率低
不释放锁会产生死锁的问题