(十)java共享数据、内置锁、死锁

1. 多窗口买票

在这里插入图片描述

多窗口售票问题
问题:剩余票数为负数
public class test1 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        ticket.setCount(100);
        TicketTask ticketTask = new TicketTask(ticket);
        Thread t1 = new Thread(ticketTask,"窗口1");
        Thread t2 = new Thread(ticketTask,"窗口2");
        Thread t3 = new Thread(ticketTask,"窗口3");
        Thread t4 = new Thread(ticketTask,"窗口4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();//同时启动四个线程
    }
}
public class Ticket {
    private int count;
    public void setCount(int count) {
        this.count = count;
    }
    public int  checkCount(){
        return this.count;
    }
    public void  outTicket(){
        this.count--;
    }
}
public class TicketTask implements Runnable {
    private Ticket ticket;
    public TicketTask() {
    }
    public TicketTask(Ticket ticket) {
        this.ticket = ticket;
    }
    @Override
    public void run() {
        while (true) {
            if (ticket.checkCount() <= 0)
                break;
            try {
                Thread.sleep(550);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            ticket.outTicket();
            System.out.println(Thread.currentThread().getName() + "\t 剩余票数:" + ticket.checkCount());
        }
    }
}
结果为:
窗口3	 剩余票数:7
窗口2	 剩余票数:4
窗口1	 剩余票数:3
窗口3	 剩余票数:2
窗口4	 剩余票数:4
窗口4	 剩余票数:1
窗口3	 剩余票数:-2
窗口1	 剩余票数:-1
窗口2	 剩余票数:-1

理想状态是当票零时,停止出售,买票任务不安全

  • 超卖问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
  • 解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

2. 临界区代码块

临界区代码段是每个线程中访问临界资源的那段代码,多个线程必须互斥地对临界区资源进行访问。线程进入临界区代码段之前,必须在进入区申请资源,申请成功之后进行临界区代码段,执行完成之后释放资源。临界区代码段(Critical Section)的进入和退出即:多个线程同时访问的代码块,比如上面的例子:

		while (true) {
            if (ticket.checkCount() <= 0)
                break;
            try {
                Thread.sleep(550);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            ticket.outTicket();
            System.out.println(Thread.currentThread().getName() + "\t 剩余票数:" + ticket.checkCount());
        }

3. 共享数据

临界区代码块中,多个线程共享访问的里面的数据

  • 一般的共享数据为堆中的数据
  • 加锁通常是给共享数据加锁

4. synchronized内置锁

请添加图片描述

5. synchronized语法

5.1 代码块

synchronized (共享对象(plus,ticket)) {//临界区代码块
    对共对象的访问(plus)(ticket)
}

比如售票买票系统

多窗口售票问题
使用synchronized上锁
public class test1 {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        ticket.setCount(100);
        TicketTask ticketTask = new TicketTask(ticket);
        Thread t1 = new Thread(ticketTask,"窗口1");
        Thread t2 = new Thread(ticketTask,"窗口2");
        Thread t3 = new Thread(ticketTask,"窗口3");
        Thread t4 = new Thread(ticketTask,"窗口4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

public class Ticket {
    private int count;
    public void setCount(int count) {
        this.count = count;
    }
    public int  checkCount(){
        return this.count;
    }
    public void  outTicket(){
        this.count--;
    }
}

public class TicketTask implements Runnable {
    private Ticket ticket;
    public TicketTask() {
    }
    public TicketTask(Ticket ticket) {
        this.ticket = ticket;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (this) {//对代码块进行上锁,锁为:该对象变量名
                if (ticket.checkCount() <= 0)
                    break;
                try {
                    Thread.sleep(550);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                ticket.outTicket();
                System.out.println(Thread.currentThread().getName() + "\t 剩余票数:" + ticket.checkCount());
            }
        }
    }
}

5.2 实例方法上锁

相当于synchronized(this)
比如对汽车的保养过程

汽车的打蜡抛光(非静态)
public class SynDemo1 {
    public static void main(String[] args) {
        Car a = new Car();
        Thread1 t1 = new Thread1(a);
        Thread2 t2 = new Thread2(a);
        t1.start();
        t2.start();
    }
}
class Thread1 extends Thread {
    private Car a;
    public Thread1(Car a) {
        this.a = a;
    }
    public void run() {
        a.fun1();
    }
}
class Thread2 extends Thread {
    private Car a;
    public Thread2(Car a) {
        this.a = a;
    }
    public void run() {
        a.fun2();
    }
}
class Car {
    public void fun1() {
        synchronized (this) {
            System.out.println("开始打蜡");
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("打蜡结束");
        }
    }
    public void fun2() {
        synchronized (this) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("开始抛光");
                System.out.println("抛光结束");
            }
        }
    }
}
开始打蜡
打蜡结束
开始抛光
抛光结束

5.3 静态方法上锁

相当于synchronized(class),

public class SynDemo1 {
    public static void main(String[] args) {
        Car a = new Car();
        Thread t1 = new Thread1(a);
        Thread t2 = new Thread2(a);
        t1.start();
        t2.start();
    }
}
class Thread1 extends Thread {
    private Car a;
    public Thread1(Car a) {
        this.a = a;
    }
    public void run() {
        a.fun1();
    }
}
class Thread2 extends Thread {
    private Car a;
    public Thread2(Car a) {
        this.a = a;
    }
    public void run() {
        a.fun2();
    }
}
class Car {
    public synchronized static void fun1() {
        System.out.println("开始打蜡");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("打蜡结束");
    }
    public static void fun2() {
        synchronized (Car.class) {
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("开始抛光");
            System.out.println("抛光结束");
        }
    }
}

6. 必须有相同的锁

public class Test1 {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        MyThread2 t2 = new MyThread2();
        t1.start();
        t2.start();
    }
}
public class MyThread1 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            synchronized (String.class) {
                System.out.println("a");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("b");
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            synchronized (Integer.class) {
                System.out.println("1");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("2");
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
否则无效

7. 死锁

Synchronized嵌套使用时,比如张三在A电话亭想去B电话亭,李四在B电话亭想去A电话亭,这时会发生死锁。
a线程锁定一个资源,同时想获取b线程的资源,b线程锁定一个资源,同时想获取a线程的资源。
此时会发生死锁

public class DeadLock {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (String.class) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    synchronized (Integer.class) {
                    }
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (Integer.class) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    synchronized (String.class) {
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}

8. CountDownLatch

juc的公共锁

  • 构造函数new CountDownLatch(10)授权数量
  • countDown()授权数量-1
  • awit()执行后线程进入阻塞状态,直到授权数量=0;
public class Plus {
    private int count;
    public int getCount() {
        return count;
    }
    public void selfPlus(){
        count++;
    }
}
public class MyTask implements Runnable {
    private Plus plus;
    public MyTask() {
    }
    public MyTask(Plus plus) {
        this.plus = plus;
    }
    CountDownLatch countDownLatch = new CountDownLatch(4);
    @Override
    public void run() {
        synchronized (plus) {
            for (int i = 0; i < 100000000; i++) {
                plus.selfPlus();
            }
            countDownLatch.countDown();
        }
    }
}
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Plus plus = new Plus();
        MyTask myTask = new MyTask(plus);
        for (int i = 0; i < 4; i++) {
            new Thread(myTask).start();
        }
        myTask.countDownLatch.await();
        System.out.println("真实值 = " + plus.getCount());
    }
}
实际值为:400000000
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值