• Java 进阶多线程(一)


    一、多线程

    1、继承Thread类(方式一)

    1)实现多线程

    继承Thread类
    重写run()方法
    创建线程对象
    调用start()方法启动

    调用run方法会当成普通方法执行,只有调用start方法才是启动一个新的线程执行

    2)优缺点

    • 优点

    编码简单

    • 缺点

    是单继承,线程类继承Thread后,不能继承其他类,不便于扩展

    2、实现Runnable接口(方式二)

    1)实现多线程

    定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
    创建MyRunnable对象
    把MyRunnable任务对象交给Thread线程对象处理
    调用线程对象的start()方法启动线程

    2)实现多线程(匿名内部类方式)

    创建Runnable的匿名内部类对象
    交给Thread处理
    调用线程对象的start()启动线程

    3)优缺点

    • 优点

    实现了Runnale接口,可以继续继承和实现

    • 缺点

    线程有执行结果是不能直接返回

    3、实现Callable、FutureTask接口(方式三)

    1)实现多线程

    创建Callable接口实现类,重写call()方法,封装
    用FutureTask把Callable对象封装成线程任务对象
    线程任务对象交给Thread处理,调用start()方法启动线程,执行任务
    执行完毕后,通过FutureTask的get方法去获取任务执行的结果

    2)优缺点

    • 优点

    线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
    可以在线程执行完毕后去获取线程执行的结果。

    • 缺点

    编码复杂

    方法名解释
    public FutureTask<>(Callable call)把Callable对象封装成FutureTask对象
    public V get() throws Exception获取线程执行call方法返回的结果

    4、Thread线程

    1)Thread的构造器

    构造器解释
    public Thread(String name) 为当前线程指定名称
    public Thread(Runnable target) 封装Runnable对象成为线程对象
    public Thread(Runnable target ,String name ) 封装Runnable对象成为线程对象,并指定线程名称

    在这里插入图片描述

    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("Runnable "+Thread.currentThread().getName()+" ===>> "+i);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    public class ClassStructure {
        public static void main(String[] args){
            MyRunnable myRunnable = new MyRunnable();
            //分配一个带有指定目标新的线程对象
            Thread thread = new Thread(myRunnable);
            //获取当前线程名称
            String name = thread.getName();
            System.out.println("当前名称-1:"+name);
            //设置新的名称
            thread.setName("Thread-1-新");
            String newName = thread.getName();
            System.out.println("当前名称-1:"+ newName);
            //启动多线程
            thread.start();
    
            MyRunnable mr = new MyRunnable();
            //分配一个带有指定目标新的线程对象并指定名称
            Thread td = new Thread(mr,"指定名称:");
            String strName = td.getName();
            System.out.println("当前名称-2:"+ strName);
            td.start();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5、Thread的方法

    1)Thread获取和设置线程名称

    方法名解释
    String getName​() 获取当前线程的名称,默认线程名称是Thread-索引
    void setName​(String name) 将此线程的名称更改为指定的名称,通过构造器也可以设置线程名称

    在这里插入图片描述

    public class ClassStructure {
        public static void main(String[] args){
            Thread thread = new Thread();
            String name = thread.getName();
            System.out.println(name);
    
            thread.setName("线程1");
            String nameNew = thread.getName();
            System.out.println(nameNew);
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2)Thread类获得当前线程的对象

    方法名解释
    public static Thread currentThread()返回对当前正在执行的线程对象的引用

    注意:

    1、此方法是Thread类的静态方法,可以直接使用Thread类调用。
    2、这个方法是在哪个线程执行中调用的,就会得到哪个线程对象。

    public class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 10; i++) {
                //获得当前正在执行的线程对象
                Thread td = Thread.currentThread();
                //获取当前线程名称
                System.out.println(td.getName() + i);
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3)Thread类的线程休眠方法:

    方法名解释
    public static void sleep(long time) 让当前线程休眠指定的时间后再继续执行,单位为毫秒

    在这里插入图片描述

    public class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 10; i++) {
                //获得当前正在执行的线程对象
                Thread td = Thread.currentThread();
                //获取当前线程名称
                System.out.println(td.getName() + i);
    
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    public class ClassStructure {
        public static void main(String[] args) throws InterruptedException {
            MyRunnable mr = new MyRunnable();
            //分配一个带有指定目标新的线程对象并指定名称
            Thread td = new Thread(mr,"指定名称:");
            String strName = td.getName();
            System.out.println("当前名称-2:"+ strName);
            System.out.println(System.currentTimeMillis());
            //暂停3秒
            Thread.sleep(3000);
            System.out.println(System.currentTimeMillis());
            td.start();
        }
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    二、线程安全

    多个线程同时访问同一个共享资源且存在修改该资源

    三、线程同步

    • 概念

    解决线程安全问题

    • 保证线程安全

    多个线程实现先后依次访问共享资源,可以解决安全问题

    • 思想

    加锁:让多个线程实现先后依次访问共享资源,可以解决安全问题

    1、同步代码块

    • 原理

    每次只能一个线程进入,执行完毕后自动解锁,其它线程才可以进来执行

    • 作用

    线程安全问题的核心代码给上锁

    • 实现线程安全

    对出现问题的核心代码是使用synchronized进行加锁
    每次只能一个线程占锁进入执行

    • 格式
    synchronized(同步锁对象) {
    	操作共享资源的代码(核心代码)
    }
    
    • 1
    • 2
    • 3
    • 同步锁对象要求

    对于实例方法建议使用this作为锁对象
    对于静态方法建议使用字节码(类名.class)对象作为锁对象

    2、同步方法

    • 原理

    每次只能一个线程进入,执行完毕后自动解锁,其它线程才可以进来执行

    • 作用

    线程安全问题的核心代码给上锁

    • 格式
    • 实现线程安全

    对出现问题的核心代码是使用synchronized进行加锁
    每次只能一个线程占锁进入执行

    修饰符 synchronized 返回值类型 方法名称(形参列表) {
    	操作共享资源的代码
    }
    
    • 1
    • 2
    • 3
    • 同步锁对象要求

    对于实例方法建议使用this作为锁对象
    对于静态方法建议使用字节码(类名.class)对象作为锁对象

    • 底层原理

    如果方法是实例方法:同步方法默认用this作为的锁对象。但是代码要高度面向对象
    如果方法是静态方法:同步方法默认用类名.class作为的锁对象

    3、Lock锁

    • 原理

    锁对象Lock,使用更加灵活、方便
    Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
    Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象

    方法名解释
    public ReentrantLock​()获得Lock锁的实现类对象
    void lock()加锁
    void unlock()解锁
  • 相关阅读:
    Flink的六种物理分区策略
    Qt源码阅读(一) 信号槽的连接与调用
    计算机设计大赛 行人重识别(person reid) - 机器视觉 深度学习 opencv python
    git笔记
    <学习笔记>从零开始自学Python-之-web应用框架Django( 十四)上线部署(阿里云+Nginx+uwsgi+MySQL)
    【HMS core】【FAQ】【Account Kit】典型问题集2
    【技术实战】Linux中的命令行【实战篇】【一】
    [ MySQL] — 事务管理
    Android修行手册 - 阴影效果的几种实现以及一些特别注意点
    来一场关于数字人的灵魂辩论|BOOK DAO内容共建招募
  • 原文地址:https://blog.csdn.net/walykyy/article/details/126708333