多线程及其生命周期和锁的介绍

多线程

介绍

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。

在这里插入图片描述

  • 什么是多线程?

​ 有了多线程,我们就可以让程序同时做多件事情

  • 多线程的作用?

​ 提高效率

  • 多线程的应用场景?

​ 只要你想让多个事情同时运行就需要用到多线程比如:软件中的耗时操作、所有的聊天软件、所有的服务器

  1. 并发:在同一时刻,有多个指令在单个 CPU 上交替执行
  2. 并行:在同一时刻,有多个指令在多个 CPU 上同时执行

多线程实现方式(多个线程交替进行)

1.继承 Thread 类实现多线程

MyThread 类

public class MyThread extends Thread{
    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "HelloWorld");
        }
    }
}

ThreadDemo 类

public class ThreadDemo {
    public static void main(String[] args) {
        /*
         * 多线程的第一种启动方式:
         * 1. 自己定义一个类继承Thread
         * 2. 重写run方法
         * 3. 创建子类的对象,并启动线程
         */

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("线程1");
        t2.setName("线程2");

        t1.start();
        t2.start();
    }
}

2.实现 Runnable 接口实现多线程

MyRun 类

public class MyRun implements Runnable{
    @Override
    public void run() {
        //书写线程要执行的代码
        for (int i = 0; i < 100; i++) {
            //获取到当前线程的对象
            //Thread t = Thread.currentThread();
            //System.out.println(t.getName() + "HelloWorld!");
            System.out.println(Thread.currentThread().getName() + "HelloWorld!");
        }
    }
}

ThreadDemo 类

public class ThreadDemo {
    public static void main(String[] args) {
        /*
         * 多线程的第二种启动方式:
         * 1.自己定义一个类实现Runnable接口
         * 2.重写里面的run方法
         * 3.创建自己的类的对象
         * 4.创建一个Thread类的对象,并开启线程
         */

        //创建MyRun的对象
        //表示多线程要执行的任务
        MyRun mr = new MyRun();

        //创建线程对象
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);

        //给线程设置名字
        t1.setName("线程1");
        t2.setName("线程2");

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

3.利用Callable接口和Future接口方式实现

MyCallable 类

import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        //求1-100之间的和
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum = sum + i;
        }
        return sum;
    }
}

ThreadDemo 类

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*
         * 多线程的第三种实现方式:
         * 特点:可以获取到多线程运行的结果
         * 1. 创建一个类MyCallable实现Callable接口
         * 2. 重写call(是有返回值的,表示多线程运行的结果)
         * 3. 创建MyCallable的对象(表示多线程要执行的任务)
         * 4. 创建FutureTask的对象(作用管理多线程运行的结果)
         * 5. 创建Thread类的对象,并启动(表示线程)
         */

        //创建MyCallable的对象(表示多线程要执行的任务)
        MyCallable mc = new MyCallable();
        //创建FutureTask的对象(作用管理多线程运行的结果)
        FutureTask<Integer> ft = new FutureTask<>(mc);
        //创建线程的对象
        Thread t1 = new Thread(ft);
        //启动线程
        t1.start();

        //获取多线程运行的结果
        Integer result = ft.get();
        System.out.println(result);
    }
}

多线程三种实现方式对比

实现方式优点缺点
继承 Thread 类编程比较简单,可以直接使用 Thread 类中的方法扩展性较差,不能再继承其他的类
实现 Runnable 接口扩展性强,实现该接口的同时还可以继承其他的类编程相对复杂,不能直接使用 Thread 类中的方法
实现 Callable 接口(补充:可获取线程执行结果)(补充:编程复杂度较高)

线程常见的成员方法

方法名称说明
String getName()返回此线程的名称
void setName(String name)设置线程的名字(构造方法也可以设置名字)
static Thread currentThread()获取当前线程的对象
static void sleep(long time)让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级
final void setDaemon(boolean on)设置为守护线程
public static void yield()出让线程 / 礼让线程
public static void join()插入线程 / 插队线程
守护线程

主线程运行完毕后守护线程也会停止,只有主线程运行时守护线程才有意义,例如在聊天界面传输文件,聊天框为主线程,传送文件为守护线程,聊天关闭文件也不会继续传输

在这里插入图片描述

注:

1、如果我们没有给线程设置名字,线程也是有默认的名字的
格式:Thread-X(X序号,从0开始的)
2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置

3.当JVM虚拟机启动之后,会自动的启动多条线程
其中有一条线程就叫做main线程
他的作用就是去调用main方法,并执行里面的代码
在以前,我们写的所有的代码,其实都是运行在main线程当中

礼让线程

让出当前cpu执行权,然后重新竞争(Thread.yield())

插入线程

插入到当前线程之前,例如在主线程使用时,它会比主线程先执行

抢占式调度

方法名称说明
setPriority(int newPriority)设置线程的优先级
final int getPriority()获取线程的优先级

注:

优先级设置等级为1-10,高优先级只是先执行的概率大,并不会百分百比低优先级的先执行

线程的生命周期

同步代码块

作用:把操作共享数据的代码锁起来,保证线程安全。

synchronized (){
    操作共享数据的代码
}

特点:

  1. 锁默认打开,有一个线程进入代码块后,锁自动关闭;
  2. 代码全部执行完毕,线程退出后,锁自动打开。
public class MyThread extends Thread{
    // 所有对象共享的票数据(static保证唯一)
    static int ticket = 0; // 0 ~ 99
    
    // 锁对象(static保证所有线程共用同一把锁)也可用当前对象的字节码
    static Object obj = new Object();

    @Override
    public void run() {
        while (true){
            // 同步代码块:用obj作为锁
            synchronized (obj){
                if(ticket < 100){
                    try {
                        Thread.sleep(10); // 模拟售票延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket++;//如果不用同步代码块,此处可能被1和2线程同时运行
                    System.out.println(getName() + "正在卖第" + ticket + "张票!!!");
                }else{
                    break; // 票卖完则退出循环
                }
            }
        }
    }
}

同步方法

定义:将synchronized关键字直接加在方法上,实现线程安全的方法。

修饰符 synchronized 返回值类型 方法名(方法参数) {
    // 方法体(会被锁住的代码)
}

特点

会锁住方法内的所有代码

锁对象不能自定义,由方法类型决定:

  • 非静态同步方法:锁对象是this(当前对象);
  • 静态同步方法:锁对象是当前类的字节码文件对象(如类名.class)。

ThreadDemo类

public class ThreadDemo {
    public static void main(String[] args) {
        /*
            需求:
                某电影院目前正在上映国产大片,共有100张票,而它有3个售票窗口
                利用同步方法完成售票
            技巧:同步代码块
        */
        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        Thread t3 = new Thread(mr);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

MyRunnable类

public class MyRunnable implements Runnable {
    private int ticket = 0; // 共享票数据

    @Override
    public void run() {
        // 1.循环售票
        while (true) {
            // 2.调用同步方法(完成售票逻辑)
            if (method()) break; // 票卖完则退出循环
        }
    }

    // 非静态同步方法:锁对象是this(当前MyRunnable对象)
    private synchronized boolean method() {
        // 3.判断票是否卖完
        if (ticket == 100) {
            return true; // 票已卖完,返回true让run()退出循环
        } else {
            // 4.模拟售票延迟
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 5.售票逻辑
            ticket++;
            System.out.println(Thread.currentThread().getName() + "卖出第" + ticket + "张票");
            return false; // 票未卖完,继续循环
        }
    }
}

注:提取方法用快捷键ctrl+alt+m

Lock锁

引入背景:同步代码块 / 方法的锁操作是隐式的,JDK5 后提供Lock锁,可显式控制加锁 / 释放锁,锁定操作更灵活。

核心特点

  • synchronized支持更广泛的锁定操作;
  • 提供显式方法:
    • void lock():主动获取锁;
    • void unlock():主动释放锁。

使用说明

  • Lock是接口,需用实现类ReentrantLock实例化;
  • ReentrantLock的构造方法:ReentrantLock() → 创建实例。

ThreadDemo

public class ThreadDemo {
    public static void main(String[] args) {
        /*
            需求:
                某电影院目前正在上映国产大片,共有100张票,而它有3个售票窗口
                用JDK5的lock实现
        */
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        MyThread t3 = new MyThread();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

MyThread

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread extends Thread {
    // 共享票数据(static保证所有线程共享)
    static int ticket = 0;
    // Lock锁实例(static保证所有线程共用同一把锁)
    static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        // 1.循环售票
        while (true) {
            // 2.显式加锁
            lock.lock();
            try {
                // 3.判断票是否卖完
                if (ticket == 100) {
                    break;
                } else {
                    // 4.模拟售票延迟
                    Thread.sleep(10);
                    // 5.售票逻辑
                    ticket++;
                    System.out.println(getName() + "在卖第" + ticket + "张票");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 6.显式释放锁(放在finally确保锁一定被释放)
                lock.unlock();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值