多线程实现类、Thread方法、线程安全、阻塞队列

本文详细介绍了Java中多线程的实现方式,包括继承Thread、实现Runnable和Callable,以及线程安全问题的分析,如卖票案例。此外,讨论了线程调度、优先级、守护线程,并探讨了死锁和生产者消费者问题,最后讲解了阻塞队列的概念和应用。
摘要由CSDN通过智能技术生成

目录

多线程概述

多线程的实现方式1 - 继承Thread

 多线程的实现方式2 - 实现Runnable

多线程的实现方式3 - 实现Callable和Future

多线程的实现方式 - 两个小问题

三种实现方式的对比

Thread方法 - 设置和获取名字

Thread方法 - 获得线程对象

 Thread方法 - sleep

Thread方法 - 线程优先级

Thread方法 - 守护线程

线程安全问题 - 卖票案例实现

 线程安全问题 - 原因分析

线程安全问题 - 同步代码块

线程安全问题 - 锁对象唯一

线程安全问题 - 同步方法

线程安全问题 - lock

死锁

生产者和消费者案例

 思路分析

代码实现:

代码改写

阻塞队列 - 基本使用

阻塞队列的继承结构?

阻塞队列 - 实现等待唤醒机制


多线程概述

什么是多线程?
    是指从软件或者硬件上, 实现多个线程并发执行的技术
    具有多线程能力的计算机, 应具有硬件支持, 而能够在同一时间执行多个线程任务, 提升性能

什么是并行? 
    在同一时刻, 有多个指令在"多个"CPU上同时执行
    举例: 多个厨师(CPU)同时炒多个菜(指令)
  
什么是并发?
    在同一时刻, 有多个指令在"单个"CPU上交替执行    
    举例: 一个厨师(CPU)同时抄多个菜(指令)
    
什么是进程?
    是一款运行的软件
        1. 独立性: 指一个能运行的基本单位, 同时也是系统分配资源和调度的独立单位
        2. 动态性: 进程的本质是程序的一次执行过程,  动态产生,动态消亡的
        3. 并发性: 任何进程都可以同时和其他进程并发执行

什么是线程?
    是进程中单个顺序控制流(软件中的一个功能), 是一条执行路径
    线程是属于进程的(一款软件中的一个功能)

单线程和多线程举例?
    单线程: 一个进程只有一条执行路径, 称为单线程
    多线程: 一个进程如果有多条执行路径, 称为多线程(迅雷,360)

多线程的实现方式1 - 继承Thread

实现1: 继承Thread类
    1. MyThread继承Thread类
    2. 重写run方法
    3. 测试类创建对象
    4. 启动线程
   
代码示例:
    class MyThreadTest{
        public static void main(String[] args) {
            //3. 测试类创建对象
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();

            //4. 启动线程: 能看到交替执行的现象
            t1.start();
            t2.start();

            //t1.run(); //普通的创建对象,通过对象调用方法,并没有开启线程
            //t2.run(); //普通的创建对象,通过对象调用方法,并没有开启线程
        }
    }

    //1. MyThread继承Thread类
    public class MyThread extends Thread{
        //2. 重写run方法
        @Override
        public void run() {
            for (int i = 1; i <= 100 ; i++) {
                System.out.println("线程执行:" + i);
            }
        }
    }

 多线程的实现方式2 - 实现Runnable

实现2: 实现Runnable接口
    1. MyRunnable实现Runnable接口
    2. 重写run方法
    3. 测试类创建MyRunnable对象
    4. 测试类创建Thread对象,将MyRunnable对象作为参数传递
    5. 启动线程: 能看到交替执行的现象

代码示例:
    class TestMyRunnable{
        public static void main(String[] args) {
            //3. 测试类创建MyRunnable对象
            MyRunnable mr = new MyRunnable();

            //4.测试类创建Thread对象,将MyRunnable对象作为参数传递
            Thread t1 = new Thread(mr);
            Thread t2 = new Thread(mr);

            //5. 启动线程: 能看到交替执行的现象
            t1.start();
            t2.start();
        }
    }


    //1. MyRunnable实现Runnable接口
    public class MyRunnable implements Runnable{
        //2. 重写run方法
        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                System.out.println("线程启动:" + i);
            }
        }
    }

多线程的实现方式3 - 实现Callable和Future

实现3: 实现Callable接口
    1. 定义MyCallable类实现Callable接口
    2. 重写call方法 -> 需要返回线程执行完毕后的结果
    3. 测试类创建MyCallable对象
    4. 测试类创建Future的实现类对象FutureTask, 将MyCallable对象作为参数传递
    5. 测试类创建Thread对象, 将FutureTask对象作为参数传递
    6. 启动线程: 能看到交替执行的现象
    
注意: 
	如果线程没有执行完毕就调用了get, 那么就会卡住死等!
    FutureTask的泛型和返回值类型匹配即可!
	
代码示例:
    class MyCallableTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //3. 测试类创建MyCallable对象
            MyCallable mc = new MyCallable();

            //4. 测试类创建Future的实现类对象FutureTask, 将MyCallable对象作为参数传递
            // 可以获取执行后的结果 -> get方法
            FutureTask<String> ft = new FutureTask<>(mc);

            // System.out.println(ft.get()); // 注意: 如果线程没有执行完毕就调用了get, 那么就会卡住死等!

            //5. 测试类创建Thread对象, 将FutureTask对象作为参数传递
            Thread t1 = new Thread(ft);

            //6. 启动线程: 能看到交替执行的现象
            t1.start();

            //7.通过FutureTask对象的get方法, 获取执行结果 -> 注意: 如果线程没有执行完毕就调用了get, 那么就会卡住死等!
            System.out.println(ft.get());
        }
    }


    //1. 定义MyCallable实现Callable接口
    public class MyCallable implements Callable {
        //2. 重写call方法 -> 需要返回线程执行完毕后的结果
        @Override
        public String call() throws Exception {
            for (int i = 1; i <= 100; i++) {
                System.out.println("第" + i + "次买彩票");
            }
            return "买彩票终于中了500万!";
        }
    }

多线程的实现方式 - 两个小问题

 两个小问题:
    1. 为什么要重写run方法?
        因为run方法是用来封装被线程执行的代码, 程序运行会执行run中的逻辑
        
    2. run方法和start方法的区别?
        如果执行run, 仅仅是普通的对象名调用方法执行, 并没有开启线程
        查看start源码
            public synchronized void start() {
            ...
            //调用了start0
            start0();
            }
            // 被native修饰的本地方法, 和本地操作系统交互, 开启线程的方法
            private native void start0();

三种实现方式的对比

 三种实现方式的对比:
    实现Runnable和Callable接口
        优点: 扩展性强, 实现接口的同时可以继承其他类
        缺点: 变的复杂, 不能直接用Thread类中的方法(没有继承关系)
    继承Thread类
        优点: 编程简单, 可以直接用Thread类中的方法
        缺点: 可扩展性差, 因为java只支持单继承
        

Thread方法 - 设置和获取名字

线程的默认的名字?
	Thread-编号从0开始

    查看Thread空参构造源码,能看到name格式
        public Thread() {
            //第三个参数就是name
            this(null, null, "Thread-" + nextThreadNum(), 0);
        }
        private static synchronized int nextThreadNum() {
            //让成员变量threadInitNumber每次自增
            return threadInitNumber++;
        }
        //然后通过this.name = name;将计算出来的名字赋值给线程的成员变量name


获取线程名称:
    String getName();

设置线程名称:
    void setName(String name);
    通过构造方法也可以设置线程名称()
        
代码示例:
    class MyThreadTest{
        public static void main(String[] args) {
            //创建对象
            //通过构造方法也可以设置线程名称()
            MyThread t1 = new MyThread("吕布");
            MyThread t2 = new MyThread("貂蝉");

            //void setName(String name);
            //t1.setName("张三");
            //t2.setName("罗翔");

            //启动线程
            t1.start();
            t2.start();
        }
    }

    //继承Thread类实现多线程
    public class MyThread extends Thread {
        //要使用带参构造设置线程名称,需要提供构造方法
        public MyThread() {
        }

        public MyThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                System.out.println(getName() + "线程执行:" + i);
            }
        }
    }

Thread方法 - 获得线程对象

获取线程对象:
	public static Thread Thread.currentThread(); 返回当前执行的线程对象

代码示例:
class MyRunnableTest {
    public static void main(String[] args) {
        //打印当前线程对象,打印名称
        System.out.println(Thread.currentThread().getName()); //main
        //创建MyRunnable对象
        MyRunnable mr = new MyRunnable();
        //创建Thread对象
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        //设置名称
        t1.setName("吕布");
        t2.setName("貂蝉");
        //启动线程
        t1.start();
        t2.start();
    }
}

//实现接口的方式,实现多线程
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            //由于没有继承Thread所以不能直接使用getName()方法
            //System.out.println(getName() + "线程执行:" + i);
            //public static Thread Thread.currentThread(); 返回当前执行的线程对象
            System.out.println(Thread.currentThread().getName()+"线程执行:"+i);
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值