线程安全问题
- 买火车票问题
package com.thread.demo;
public class Demo3 implements Runnable {
/* 买火车票问题
* 多个线程操作同一个资源时,出现线程安全问题,发生数据紊乱
* */
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0)
{
break;
}
try {
Thread.sleep(200); // 模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
}
}
public static void main(String[] args) {
Demo3 demo3 = new Demo3();
new Thread(demo3,"小明").start();
new Thread(demo3,"小军").start();
new Thread(demo3,"黄牛").start();
}
}
线程同步
- 并发:同一个对象被多个线程被同时操作
- 上万人同时抢100张票
- 两个银行同时取钱
- 可以通过 synchronized关键字对方法或者代码块进行修饰,让其成为同步方法或者同步代码块
package com.thread.demo;
public class Demo17 implements Runnable {
/* 买火车票问题
* 多个线程操作同一个资源时,出现线程安全问题,发生数据紊乱
* */
private int ticketNums = 100;
@Override
public void run() {
while (true) {
if (buyTickets()) break;
}
}
private synchronized boolean buyTickets() {
// 买票
if (hasTicketsOne()) return true; //判断是否有票
sleepSomeTime(); // 模拟延时
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
return false;
}
private void sleepSomeTime() {
try {
Thread.sleep(50); // 模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private boolean hasTicketsOne() {
// 判断是否有票
if (ticketNums <= 0)
{
return true;
}
return false;
}
public static void main(String[] args) {
Demo17 demo17 = new Demo17();
Thread you = new Thread(demo17, "牛逼的你们");
Thread me = new Thread(demo17, "苦逼的我");
Thread tmd = new Thread(demo17, "可恶的黄牛党");
you.setPriority(Thread.NORM_PRIORITY);
tmd.setPriority(8);
me.setPriority(Thread.MIN_PRIORITY);
you.start();
me.start();
tmd.start();
}
}
-
同步方法虽然使得线程变得安全了,但是运行效率大大降低!!
-
所以我们只对发生改变的那一块上锁
-
扩充—JUC安全类型的集合
package com.thread.demo; import java.util.concurrent.CopyOnWriteArrayList; /*测试JUC安全类型集合*/ public class Demo18 { public static void main(String[] args) throws InterruptedException { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); for (int i = 0; i <10000 ; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }
死锁
- 死锁:多个线程互相抱着对方需要的资源,形成僵持
- 产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程所使用
- 请求与保护条件:一个进程因请求资源而阻塞时,对已获取的资源保持不放
- 不剥夺条件:进程已获得资源,再未使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接等待资源的关系。
- 避免死锁只要想办法破坏其中一个或多个条件即可避免。
package com.thread.demo;
public class Demo19 {
public static void main(String[] args) {
Child child1 = new Child("小明", 1);
Child child2 = new Child("小军", 2);
new Thread(child1).start();
new Thread(child2).start();
}
}
class Toys {
private String name;
public Toys(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
class Child extends Thread {
private int chose;
static Toys car = new Toys("玩具车"); // 静态保证资源只有一份
static Toys gun = new Toys("玩具枪");
public Child(String name, int chose) {
super(name);
this.chose = chose;
}
@Override
public void run() {
try {
play(this.chose);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void play(int i) throws InterruptedException {
if (this.chose == 1) {
synchronized (car) { // 小明抢到了玩具车,然后再去抢小军的玩具枪,互相僵持 --> 死锁
System.out.println(getName() + "获得了" + car.toString());
Thread.sleep(3000);
synchronized (gun) {
System.out.println(getName() + "获得了" + gun.toString());
}
}
} else if (this.chose == 2) {
synchronized (gun) { // 小军抢到了玩具枪,然后再去抢小明的玩具车,互相僵持 --> 死锁
System.out.println(getName() + "获得了" + gun.toString());
Thread.sleep(3000);
synchronized (car) {
System.out.println(getName() + "获得了" + car.toString());
}
}
}
}
}
- 破坏条件2
package com.thread.demo;
public class Demo19 {
public static void main(String[] args) {
Child child1 = new Child("小明", 1);
Child child2 = new Child("小军", 2);
new Thread(child1).start();
new Thread(child2).start();
}
}
class Toys {
private String name;
public Toys(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
class Child extends Thread {
private int chose;
static Toys car = new Toys("玩具车"); // 静态保证资源只有一份
static Toys gun = new Toys("玩具枪");
public Child(String name, int chose) {
super(name);
this.chose = chose;
}
@Override
public void run() {
try {
play(this.chose);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void play(int i) throws InterruptedException {
if (this.chose == 1) {
synchronized (car) { // 小明抢到了玩具车
System.out.println(getName() + "获得了" + car.toString());
Thread.sleep(3000);
}// 破坏条件2,释放玩具车给小军
synchronized (gun) {
System.out.println(getName() + "获得了" + gun.toString());
}
} else if (this.chose == 2) {
synchronized (gun) { // 小军抢到了玩具枪
System.out.println(getName() + "获得了" + gun.toString());
Thread.sleep(3000);
}// 破坏条件2,释放玩具枪给小明
synchronized (car) {
System.out.println(getName() + "获得了" + car.toString());
}
}
}
}