1.什么情况下会出现线程安全问题
当多个线程操作同一个资源时,则会出现线程安全问题。
2. java如何解决线程安全问题
第一种:使用synchronized自动锁。
第二种:使用Lock手动锁
使用锁相对于原来的异步操作转换为同步操作。
2.1 使用synchronized关键字解决
它可以使用在方法上,也可以使用在代码块中。
synchronized(共享锁对象){
同步代码块。
}
public class TickRunnable implements Runnable{
private int tick=100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (tick>0){
tick--;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "卖了一张票" + "剩余:" + tick + "票。");
}else {
break;
}
}
}
}
}
public synchronized void sell(){
if (tick > 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
tick--;
System.out.println(Thread.currentThread().getName() + "卖了一张票;剩余:" + tick + "张");
}
}
2.2 使用Lock手动
Lock是一个接口,它的实现类是:ReentranLock。
public class LockThread implements Runnable{
private int tick=100;
private Lock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁
if (tick > 0) {
tick--;
try {
Thread.sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "卖了一张票" + "剩余:" + tick + "票。");
} else {
break;
}
} finally {
lock.unlock();//解锁
}
}
}
}
2.3 synchronized与lock的区别
1.synchronized可以使用在方法上和代码块。自动加锁和释放锁。不会出现死锁问题。
2.lock只能出现在代码块中,需要手动的加锁和释放锁,如果不释放锁,则会出现死锁问题,但它相对于synchronized更加灵活,它的释放放在finally中。
3.死锁
多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。举个例子,线程A拥有锁资源a,希望获取锁资源b,线程B拥有锁资源b,希望获取锁资源a。 两个线程互相拥有对方希望获取的锁资源。可能会出现程序堵塞。从而造成死锁。
解决:
1.不要使用锁嵌套。
2.设置超时时间:Lock中的tryLock。
3.使用安全java.util.concurrent下的类。
4. 线程通信
wait方法和notify方法。
(如何确定,俩个调用的都是一个卡呢,可以利用构造函数,把同一个卡的对象传进来。)
public class BankCard {
private double balance;
private boolean flag;//true:有钱 false:没钱
//存钱
public synchronized void save(double money) throws InterruptedException {
if (flag){
//属于Object类中。 进入等待队列,并且释放拥有的锁
wait();
}
balance+=money;
flag=true;
notify();
System.out.println(Thread.currentThread().getName()+"往卡中存入了:"+money+";卡中余额:"+balance);
}
//取钱
public synchronized void withdraw(double money) throws InterruptedException {
if (!flag){
wait();
}
balance-=money;
flag=false;
notify();
System.out.println(Thread.currentThread().getName()+"从卡中取出了:"+money+";卡中余额:"+balance);
}
}
public class SaveThread extends Thread{
private BankCard bankCard;
public SaveThread(BankCard bankCard){
this.bankCard=bankCard;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
bankCard.save(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class WithdrawThread extends Thread{
private BankCard bankCard;
public WithdrawThread(BankCard bankCard){
this.bankCard=bankCard;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
bankCard.withdraw(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Test {
public static void main(String[] args) {
BankCard bankCard=new BankCard();
SaveThread saveThread=new SaveThread(bankCard);
saveThread.setName("张三");
WithdrawThread withdrawThread =new WithdrawThread(bankCard);
withdrawThread.setName("李四");
saveThread.start();
withdrawThread.start();
}
}
5.线程状态
NEW,====新建状态。
RUNNABLE,===>就绪状态和运行状态
BLOCKED,===>堵塞状态
WAITING,====>等待状态
TIMED_WAITING,===>时间等待
TERMINATED;===终止。
通过调用不同的方法相互转换。