27. 多线程

 多线程的创建

方式一:继承Thread类

public class Test {
    public static void main(String[] args) {
        // 3. new一个线程对象
        Thread t = new MyThread();
        // 4. 调用start方法启动线程(执行的还是run)
        t.start();

        for (int i = 0; i < 5; i++) {
            System.out.println("主线程执行输出:" + i);
        }
    }
}

/*
    1. 定义一个线程类继承Thread类
 */
class MyThread extends Thread {
    /**
        2. 重写run方法,里面定义线程以后要干啥
     */
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程输出第:" + i + "次");
        }
    }
}

方式二:实现Runnable接口

public class Test {
    public static void main(String[] args) {
        //3. 创建一个任务对象
        Runnable target = new MyThread();
        //4. 把任务对象交给Thread处理
        Thread t = new Thread(target);
        //5. 启动线程
        t.start();
        for (int i = 0; i < 500; i++) {
            System.out.println("主线程执行输出: " + i);
        }
    }
}

/*
    1. 定义一个线程任务类 实现Runnable接口
 */
class MyThread implements Runnable{
    /**
     * 2. 重写run方法,定义线程的执行任务的
     */
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("子线程执行输出: " + i);
        }
    }
}
/*
    匿名内部类实现
 */
public class Test {
    public static void main(String[] args) {
        new Thread(() ->{
                for (int i = 0; i < 500; i++)
                    System.out.println("子线程执行输出: " + i);
        }).start();
        for (int i = 0; i < 500; i++) {
            System.out.println("主线程执行输出: " + i);
        }
    }
}

方式三:JDK5.0新增:实现Callable接

public class Test {
    public static void main(String[] args) {
        // 3. 创建Callable对象
        Callable<String> call = new MyCallable(100);
        // 4.把Callable任务对象交给FutureTask对象
        //   FutureTask对象的作用1:
        //   是Runnable的对象(实现了Runnable接口),可以交给Thread了

        //   FutureTask对象的作用2:
        //   可以在线程执行完毕之后通过调用其get方法得到线程执行完成的结果
        FutureTask<String> ft = new FutureTask<String>(call);

        // 5. 交给线程处理
        Thread t1 = new Thread(ft);
        // 6. 启动线程
        t1.start();

        try {
            String rs1 = ft.get();
            System.out.println("第一个结果:" + rs1);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

/**
    1. 定义一个任务类实现Callable接口
        应该声明线程任务执行完毕后结果的数据类型
 */
class MyCallable implements Callable<String> {
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    /*
        2. 重写call方法(线程的任务方法)
     */
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i;
        }
        return "子线程的累加和为:" + sum;
    }
}

总结 

Thread的常用方法

 

public class Test {
    public static void main(String[] args) {
        Thread t1 = new MyCallable("一号");
        t1.start();
        System.out.println(t1.getName());

        Thread t2 = new MyCallable("二号");
        t2.start();
        System.out.println(t2.getName());

        // 哪个线程执行它,它就得到哪个线程
        // 主线程名称就叫main
        Thread m = Thread.currentThread();
        System.out.println(m.getName());

        for (int i = 0; i < 5; i++) {
            System.out.println("main线程输出" + i);
        }
    }
}

/**
    1. 定义一个任务类实现Callable接口
        应该声明线程任务执行完毕后结果的数据类型
 */
class MyCallable extends Thread {
    public MyCallable() {
    }
    
    // 为当前线程对象设置名称,送给父类的有参构造函数
    public MyCallable(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "子线程" + i);
        }
    }
}

Thread.sleep(5000);

线程安全

线程安全问题是什么、发生的原因

 

线程安全问题案例模拟

public class Account {
    private String cardId;
    private double money; //账户余额
    
    public Account(String cardId ,double money) {
        this.cardId = cardId;
        this.money = money;
    }
    /**
        开始取钱
     */
    public void drawMoney(double money) {
        // 1.获取是谁来取钱
        String name = Thread.currentThread().getName();
        // 判断账户是否有钱
        if (this.money >= money) {
            // 2.取钱
            System.out.println(name + "取钱成功");
            System.out.println("取出 " + money + "元");
            System.out.println();
            // 3.更新余额
            this.money -= money;
            System.out.println("当前余额为:" + this.money);
        }else {
            System.out.println(name + " 余额不足");
            System.out.println();
        }
    }
}
/**
    取钱的线程类
 */
public class DrawThread extends Thread {
    // 接收处理账户对象
    private Account acc;
    public DrawThread(Account acc ,String name) {
        super(name);
        this.acc =acc;
    }
    @Override
    public void run() {
        // 小明、小红:取钱
        acc.drawMoney(100000.0);
    }
}
public class Test {
    public static void main(String[] args) {
        // 1. 定义线程类,创建一个账户对象
        Account acc = new Account("001",100000.0);

        // 2. 创建两个线程对象代表两个人同时进来了
        new DrawThread(acc ,"小明").start();
        new DrawThread(acc ,"小红").start();
    }
}
结果

小明取钱成功
小红取钱成功
取出 100000.0元

取出 100000.0元

当前余额为:-100000.0
当前余额为:0.0

线程同步

同步思想概述

方式一:同步代码块

    public void drawMoney(double money) {
        // 1.获取是谁来取钱
        String name = Thread.currentThread().getName();
        // 同步代码块
        // this == acc的共享账户
        synchronized (this) {
            // 判断账户是否有钱
            if (this.money >= money) {
                // 2.取钱
                System.out.println(name + "取钱成功");
                System.out.println("取出 " + money + "元");
                System.out.println();
                // 3.更新余额
                this.money -= money;
                System.out.println("当前余额为:" + this.money);
            }else {
                System.out.println(name + " 余额不足");
                System.out.println();
            }
        }
    }
}

方式二:同步方法

    /**
        开始取钱
     */
    public synchronized void drawMoney(double money) {
        // 1.获取是谁来取钱
        String name = Thread.currentThread().getName();
        // 判断账户是否有钱
        if (this.money >= money) {
            // 2.取钱
            System.out.println(name + "取钱成功");
            System.out.println("取出 " + money + "元");
            System.out.println();
            // 3.更新余额
            this.money -= money;
            System.out.println("当前余额为:" + this.money);
        } else {
            System.out.println(name + " 余额不足");
            System.out.println();
        }
    }
}

方式三:Lock锁

 

    /**
        开始取钱
     */
    // final修饰的成员变量唯一且不可被替换
    private final Lock lock = new ReentrantLock();
    public void drawMoney(double money) {
        // 1.获取是谁来取钱
        String name = Thread.currentThread().getName();
        // 判断账户是否有钱
        lock.lock(); // 上锁
        try {
            if (this.money >= money) {
                // 2.取钱
                System.out.println(name + "取钱成功");
                System.out.println("取出 " + money + "元");
                System.out.println();
                // 3.更新余额
                this.money -= money;
                System.out.println("当前余额为:" + this.money);
            } else {
                System.out.println(name + " 余额不足");
                System.out.println();
            }
        } finally {
            lock.unlock(); // 解锁
        }
    }
}

线程通信[了解]

public class Phone{
    // 实现线程间通信,默认为手机当前处于等待来电提醒
    private boolean flag = false;

    public void run() {
        // a. 负责来电提醒的线程
        new Thread(() -> {
            try {
                while (true) {
                    synchronized (Phone.this) {
                        if (!flag) {
                            // 代表要来电提醒了
                            System.out.println("有新电话接入,请接听");
                            flag = true;
                            Phone.this.notify();
                            Phone.this.wait();
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();

        // b. 接电话线程,正式接通了
        new Thread(() -> {
            try {
                while (true) {
                    synchronized (Phone.this) {
                        if (flag) {
                            // 可以接通电话了
                            System.out.println("通话结束");
                            Thread.sleep(1000);
                            flag = false; // 代表继续等待呼入电话
                            //唤醒别人,等待自己
                            Phone.this.notify();
                            Phone.this.wait();
                        }else {
                            Phone.this.notify();
                            Phone.this.wait();

                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void main(String[] args) {
        // 1. 创建一部手机对象
        Phone huawei = new Phone();
        huawei.run(); // 开机了

    }
}

线程池[重点]

线程池概述

线程池实现的API、参数说明

线程池处理Runnable任务

线程池处理Callable任务

Executors工具类实现线程池

public class Test {
    public static void main(String[] args) {
        // 1. 创建固定线程数据的线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable());
        pool.execute(new MyRunnable()); // 已经没有多余的线程了
    }
}

定时器

 

public class Time {
    public static void main(String[] args) {
        // 1. 创建Timer定时器
        Timer timer = new Timer(); // 定时器本身就是一个单线程
        // 2. 调用方法处理定时任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "执行一次");
            }
        },3000,2000);

    }
}

补充知识:生命周期、并发并行

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值