X04多线程部分01

线程不安全案例

package ksdxc.d19;
// 线程不安全案例:线程不安全,有负数
public class d19 {
    public static void main(String[] args) {
        BuyTicket customer = new BuyTicket();

        new Thread(customer,"a").start();
        new Thread(customer,"b").start();
        new Thread(customer,"c").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNum = 10;
    private boolean flag = true;

    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void buy() throws InterruptedException{
        if (ticketNum <= 0){
            flag = false;
            return;
        }
        // 延时
        Thread.sleep(120);
        System.out.println(Thread.currentThread().getName());
        ticketNum--;
    }
}
package ksdxc.d19;

public class bank {

    public static void main(String[] args) {
        Account account = new Account(66,"pool");

        Withdrawal withdrawal1 = new Withdrawal(account,55,"coding");
        Withdrawal withdrawal2 = new Withdrawal(account,55,"walking");

        withdrawal1.start();
        withdrawal2.start();
    }
}
/*
账户类
 */
class Account{
    int remainMoney; // 余额
    String cardName; // 银行卡类别

    public Account(int remainMoney, String cardName) {
        this.remainMoney = remainMoney;
        this.cardName = cardName;
    }
}
/*
取款类
 */
class Withdrawal extends Thread{
    Account account;
    int drawMone; // 取了多少钱
    String threadName; // 现金

    /*
    构造方法
     */
    public Withdrawal(Account account,int drawMone,String threadName){
        super(threadName);
        this.account = account;
        this.drawMone = drawMone;
    }
    /*
    取钱
     */
    @Override
    public void run() {
        // 判断有没有前
        if (account.remainMoney - drawMone < 0){
            System.out.println(Thread.currentThread().getName() + "operator fail");
            return;
        }
        try {
            Thread.sleep(120);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 程序能够执行到这里说明取款成功
        account.remainMoney = account.remainMoney - drawMone;

        System.out.println(Thread.currentThread().getName());
        System.out.println(account.remainMoney);
        /*
        输出结果出现了负数,说明线程不安全
         */
    }
}

生产者消费者模式

package du;

import java.util.ArrayList;
import java.util.List;

public class d802 {
    public static void main(String[] args) {
        /*
        wait()方法
        notify()方法
        这两种方法是Object类中自带的
        不是通过线程对象调用的

        wait()方法作用
        Object object = new Object();
        object.wait();
        表示让正在object对象上活动的线程进入等待状态
        并释放之前占用object对象的锁
        无期限等待,直到被唤醒为止

        notify()方法作用
        object.notify();
        可以让object上等待的线程进入活动状态

            两者建立在线程同步的基础之上
            因为多线程要操作一个仓库
            存在线程安全问题

        案例:
        使用wait和notify方法实现生产者和消费者模式
        这是一种特殊的业务需求
        需要做到这种效果
        生产一个,消费一个
         */

        List list = new ArrayList<>();

        Thread thread1 = new Thread(new Productor(list));
        Thread thread2 = new Thread(new Consumer(list));
        thread1.setName("生产者线程");
        thread2.setName("消费者线程");
        thread1.start();
        thread2.start();
    }


}

/*
生产线程
 */
class Productor implements Runnable{
    /*
    仓库
     */
    private List list;

    public Productor(List list){
        this.list = list;
    }

    @Override
    public void run() {
        while (true){
            /*
            两个线程都操作list集合
            所以给list加锁
             */
            synchronized (list){
                if (list.size() > 0){
                /*
                生产者生产完毕
                线程进入等待状态
                等待消费者消费
                释放list集合的锁
                 */
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                /*
                程序能够运行到这里说明.
                仓库空了可以生产了
                 */
                Object o = new Object();
                list.add(o);
                System.out.println(Thread.currentThread().getName() + " " + o);
                /*
                唤醒消费者消费
                进入消费者run方法
                两个线程会被同时唤醒
                之后两个线程开始抢占时间片
                这个时候谁先执行同步代码块都有可能
                但由于if语句限制
                必然有一个线程会再次陷入等待然后释放锁
                 */
                list.notifyAll();
            }
        }
    }
}

/*
消费线程
 */
class Consumer implements Runnable{
    /*
    仓库
     */
    private List list;

    public Consumer(List list){
        this.list = list;
    }


    @Override
    public void run() {
        /*
        按理说
        进入while循环的第1次不会走if里面
        第2次while循环会进入if条件语句当中
        然后线程就会进入等待状态
         */
        while (true){
            synchronized (list){
                if (list.size() == 0){
                    try {
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                /*
                程序能够执行到此处说明
                仓库里面有数据了
                可以开始消费
                 */
                Object remove = list.remove(0);
                System.out.println(Thread.currentThread().getName() + " " + remove);
                /*
                唤醒生产者生产
                进入生产者run方法
                两个线程会被同时唤醒
                之后两个线程会开始抢占时间片
                但由于if语句限制
                必然有一个线程会再次陷入等待然后释放锁
                 */
                list.notifyAll();
            }
        }
    }
}

同步锁的使用

    /*
    Java三大变量
        实例变量:存储堆中
        静态变量:存储方法区
        局部变量:存储栈中

        以上3大变量中,局部变量永远不会出现线程安全问题
            因为局部变量存储在栈中,永远不会共享
            一个线程一个栈
        堆和方法区是多线程共享的
            所以可能出现线程安全问题
        常量同样没有线程安全问题
            因为常量不可修改

    出现线程安全问题的3个条件
        1. 多线程并发
        2. 有共享数据
        3. 共享数据有修改行为

    synchronized同步代码块3种使用方式:
        1. 包裹线程不安全的代码
        2. 包裹不安全的方法,效率比第1种方式低
        3. 直接修饰实例方法,锁的一定是this,这总方式不灵活
           而且该方式表示整个方法都需要同步,可能导致无故扩大同步范围
           程序运行效率降低,所以不常用
        4. Vector类由于大量使用第3种方式,所以不常用
     */
	/*
    synchronized的3种写法
        1. 同步代码块
            灵活
            synchronized(线程共享对象){
                同步代码块
            }
        2. 实例方法上使用
            共享对象一定是this
            同步代码块是整个方法体
            用的是对象锁
            对象锁每个对象都有一把锁
        3. 在静态方法上使用
            表示类锁
            类锁永远只有1把
            此时无论你new了多少个对象
            静态方法上加synchronized关键字
            都需要等
            保证静态变量的安全

    /*
    开发中如何解决死锁问题:
        死锁问题很难调试
        因为程序员不知道自己在哪里错了
        所以synchronized在开发中不要嵌套使用
        synchronized会降低用户的吞吐量,也就是并发量
        电商平台的秒杀功能尤其注重高并发 也就是异步
        用户不可能排队去抢商品吧
        下面是方案优先级:
        1. 尽量使用局部变量来代替实例变量和静态变量(首选)
        2. 如果必须是实例变量,可以创建多个对象
           一个线程对应一个对象
           线程就不共享了
        3. 如果不能使用局部变量,对象也不能创建多个
           那就只能使用synchronized了
     */

数据定时备份

package du;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class d800 {
    public static void main(String[] args) throws Exception {
        /*
        定时器概述
            间隔特定的时间,执行特定的程序
            例如:
                每周需要对银行进行总账操作
                每天需要对数据进行备份操作
            可以采用多种方法实现:
                1. sleep方法,最原始的方法,比较low
                2. java类库中,已经写好了一个定时器 java.util.Timer
                   SpringTask框架的底层实现原理
         */
        /*
        创建定时器对象
         */
        Timer timer = new Timer();
        /*
        param1 定时任务
        param2 第1次执行时间
        param3 间隔多久执行1次
         */
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        /*
        第一次执行时间
         */
        Date firstExecTime = simpleDateFormat.parse("2021-09-27 20:05:00");

        timer.schedule(new LogTimerTask(),firstExecTime,1000*5);
    }
}

/*
由于TimerTask是抽象类
所以不能new对象
只能自己编写定时任务类
假设是一个记录日志的定时任务
这里可以采用匿名内部类的方式
 */
class LogTimerTask extends TimerTask {
    @Override
    public void run() {
        /*
        这里编写需要执行的任务
        获取当前时间
        进行数据备份
         */
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String string = simpleDateFormat.format(new Date());
        System.out.println(string + "finish data archived");
    }
}

合理终止线程

public class d774 {
    public static void main(String[] args) {
        MyThread1 myThread1 = new MyThread1();
        Thread thread = new Thread(myThread1);

        thread.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        /*
        终止线程
         */
        myThread1.flag = false;
    }
}

class MyThread1 implements Runnable{
    boolean flag = true;
    @Override
    public void run() {
        for (int i = 0; i < 11; i++) {
            if (flag){
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                return;
            }
        }
    }
}

总目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

muskfans

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值