买票问题:
package myRunable_1;
public class MyRunnable implements Runnable{
private int num = 100;
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+": 卖出第:" +num--+"张票");
}
}
}
}
测试类
public class MyTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable,"窗口一:");
Thread thread2 = new Thread(myRunnable,"窗口二:");
Thread thread3 = new Thread(myRunnable,"窗口三:");
//好处,可以实现多个接口
thread1.start();
thread2.start();
thread3.start();
}
}
发现问题:同票问题
问题2:卖出不存在的票
原因:
// 线程抢夺cpu 执行权时,执行的代码具有原子性
// 原子性是指最基本的代码(最小的语句)
线程的运行是抢占式的,出现负票首先是,线程1,2,3都在等于1的时候进去了while循环,导致多次输出
解决
public class My_Thread_Optimal extends Thread{
private static int ticket = 100;
private String str = "";
public My_Thread_Optimal(String name) {
super(name);
}
@Override
public void run() {
try {
//睡眠100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//只允许一个程序同步,解决重复票的问题
synchronized (str){
while (ticket>0){
//解决最后卖出不存在票的问题
if (ticket==0){
break;
}
System.out.println(getName()+": 卖出第:" +ticket+"张票");
ticket--;
}
}
}
}
当线程出现不安全情况,我们就可以用同步锁来将其锁住,只让一个线程去使用公共的资源。
synchronized ( obj ){}
参数可以是任意的对象,他的作用就是提供一个钥匙,谁有钥匙就可以打开锁。这样就解决了卖出同票的问题。
和数据改变的语句分开,在判断是否有不存在的票。解决负票的问题。
public void run() {
while (true){
lock.lock(); //上锁
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"第"+ticket+"张票");
ticket--;// 0
}
lock.unlock(); //释放锁
}
}
lock锁使用灵活,不需要参数。
synchronized jvm级别的锁,jvm自动上锁和解锁
Lock锁java代码写的锁,需要手动的加锁和释放锁
多线程产生死锁的四个必要条件:
互斥条件:一个资源每次只能被一个进程使用。
保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
不可剥夺调用:进程已获得资源,在未使用完成前,不能被剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
新建:创建线程对象
就绪:有执行资格,但是没有拿到执行权
运行:有执行资格,且有执行权
阻塞: 没有执行资格,没有执行权; 恢复阻塞回到就绪状态
死亡: 线程变成垃圾,等待回收
正常思路:
生产者:
先看有没有数据,有就等待,没有就生产,生产后,通过消费者来消费数据
消费者:
先看有没有数据,有就消费,没有就等待,且通知生产都生产数据
为了实现上述的问题,Java提供一种机制,等待唤醒机制
案例:
分析:
Account对象 (money)
SetMoney对象 (生产者)
GetMoney对象(消费者)
Test(测试类)
Account:
public class Account {
private int money;
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
SetMoney:
import java.util.Scanner;
public class SetMoney implements Runnable{
Account a = new Account();
public SetMoney(Account a) {
this.a = a;
}
public SetMoney() {
}
@Override
public void run() {
while (true){
synchronized (a){
if (a.getMoney()>0){//有钱,不设置
try {
a.wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//没钱,设置钱
int money=0;
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要存多少钱:");
money=scanner.nextInt();
if (a.getMoney()<=0){
a.notify();
a.setMoney(money);
System.out.println(Thread.currentThread().getName() + "存入" + a.getMoney());
}
a.notify();
}
}
}
}
GetMoney:
import java.util.Scanner;
public class GetMoney implements Runnable{
private Account a = new Account();
public GetMoney(Account a) {
this.a = a;
}
public GetMoney() {
}
@Override
public void run() {
// Scanner scanner = new Scanner(System.in);
// int getMoney = scanner.nextInt();
// System.out.println("请输入要取多少:");
while (true) {
synchronized (a) {
if(a.getMoney()<=0){//没钱,等待
try {
a.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if ((a.getMoney() - 100 < 0)) {
System.out.println("余额不足,请管理先存入");
a.notify();
break;
}
System.out.println(Thread.currentThread().getName() + "取走:" + a.getMoney()+ "元!");
a.setMoney(a.getMoney() - 100);
System.out.println("剩余:" + a.getMoney() + "元");
a.notify();//唤醒线程
} //运行到这里,证明原来有钱,flag是true,下面取走,要变false;
}
}
}
Test:
public class Test {
public static void main(String[] args) {
Account a = new Account();
SetMoney setMoney = new SetMoney(a);
GetMoney getMoney = new GetMoney(a);
//设置钱
Thread setThread = new Thread(setMoney,"管理");
//取钱
Thread getThread1 = new Thread(getMoney,"张三");
Thread getThread2 = new Thread(getMoney,"李四");
Thread getThread3 = new Thread(getMoney,"王五");
setThread.start();
getThread1.start();
getThread2.start();
getThread3.start();
}
}
常见情况:
新建 -- 就绪 -- 运行 -- 死亡
新建 -- 就绪 -- 运行 -- 就绪 -- 运行 -- 死亡
新建 -- 就绪 -- 运行 -- 等待阻塞 -- 同步阻塞 -- 就绪 -- 运行--死亡
新建 -- 就绪 -- 运行 -- 其它阻塞 -- 就绪 -- 运行--死亡
新建 -- 就绪 -- 运行 -- 同步阻塞 -- 就绪 -- 运行--死亡