Java多线程03
线程同步
并发:多个线程访问同一个对象,造成数据不安全
三个例子
package com.guanxing.syn;
public class UnsafeTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station, "苦逼的我").start();
new Thread(station, "牛逼的你").start();
new Thread(station, "可恶的黄牛党").start();
}
}
class BuyTicket extends Thread {
//票
private int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
while (flag) {
buy();
}
}
//买票的方法
public void buy() {
//判断是否有票
if (ticketNums <= 0) {
flag = false;
return;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票
System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNums--+"张票");
}
}
package com.guanxing.syn;
//不安全的取钱
//两个人去银行取钱
public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account(100, "结婚基金");
Drawing jay = new Drawing(account, 50, "jay");
Drawing kelly = new Drawing(account, 100, "kelly");
jay.start();
kelly.start();
}
}
//账户
class Account {
int money; //余额
String name; //卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread {
Account account; //账户
int drawingMoney; //取了多少钱
int nowMoney; //手上的钱
//构造器
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱
@Override
public void run() {
//判断余额
if (account.money-drawingMoney<0) {
System.out.println(Thread.currentThread().getName()+"钱不够了!");
return;
}
//模拟延迟 放大问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额 = 余额 - 取的钱
account.money -= drawingMoney;
nowMoney += drawingMoney;
//你卡里的钱
System.out.println(account.name+"余额为:"+account.money);
//你手里的钱
System.out.println(this.getName()+"手里的钱为:"+nowMoney);
}
}
/*
结婚基金余额为:50
结婚基金余额为:-50
jay手里的钱为:50
kelly手里的钱为:100
*/
package com.guanxing.syn;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size()); //9617 没有sleep sleep5s 9999
}
}
死锁
某一个同步块同时拥有 “ 两个以上对象的锁 ” 时,就可能发生 “ 死锁 ” 现象。
两个或多个线程都在等待对方释放资源,都停止运行的情况
产生死锁的四个必要条件(想办法打破一个就可避免!):
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能抢行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
package com.guanxing.syn;
//死锁:多个线程互相抱着对方需要的资源,形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup joey = new Makeup(0, "joey");
Makeup kane = new Makeup(1, "kane");
joey.start();
kane.start();
}
}
//口红
class Lipstick {
}
//镜子
class Mirror {
}
class Makeup extends Thread {
//口红和镜子(static保证都只有一个)
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
//化妆者的选择和名字
int choice;
String girlName;
//构造器
public Makeup (int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆的方法,互相持有对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {//获得口红的锁
System.out.println(this.girlName+"抢到口红!");
Thread.sleep(1000);
synchronized (mirror) {//抱着口红的锁想获得镜子的锁
System.out.println(this.girlName+"抢到镜子了!");
}
}
} else {
synchronized (mirror) {
System.out.println(this.girlName+"抢到镜子!");
Thread.sleep(2000);
synchronized (lipstick) {
System.out.println(this.girlName+"抢到口红了!");
}
}
}
}
}
解决方法:同时只能拿着一把锁
//化妆的方法,互相持有对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {//获得口红的锁
System.out.println(this.girlName+"抢到口红!");
Thread.sleep(1000);
}
synchronized (mirror) {//抱着口红的锁想获得镜子的锁
System.out.println(this.girlName+"抢到镜子了!");
}
} else {
synchronized (mirror) {
System.out.println(this.girlName+"抢到镜子!");
Thread.sleep(2000);
}
synchronized (lipstick) {
System.out.println(this.girlName+"抢到口红了!");
}
}
}
Lock(锁)
显示定义同步锁对象来实现同步,手动开启和关闭锁
java.util.concurrent.locks.Lock 接口是控制多个线程对共享资源进行访问的工具
ReentrantLock 类实现了 Lock(re-entrant 可重入锁)
Lock只有代码块锁,syn有代码块锁和方法锁
package com.guanxing.syn;
import java.util.concurrent.locks.ReentrantLock;
//测试Lock锁
public class TestLock {
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(myLock).start();
new Thread(myLock).start();
new Thread(myLock).start();
}
}
class MyLock implements Runnable {
//票
int ticketNum = 1000;
//定义Lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock(); //加锁
if (ticketNum>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNum--);
}else {
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}