多线程及相关概念、三种实现方式、常见成员方法及对应的练习(含笔记)

多线程&JUC

1、多线程概述

线程

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

简单理解:应用软件中互相独立,可以同时运行的功能

进程

进程是程序的基本执行实体(任务管理器中每个软件运行之后本身就是一个进程,是独立运行的程序)

一、进程和线程的区别
  1. 定义

    1. 进程:操作系统资源分配的最小单位,是独立运行的程序。

    2. 线程:进程内的执行单元,CPU 调度执行的最小单位,一个进程可包含多个线程。

  2. 资源

    1. 进程:拥有独立内存、资源,进程间互不共享。

    2. 线程:共享所在进程的内存与资源,仅栈、程序计数器私有。

  3. 开销

    1. 进程:创建、销毁、切换开销大。

    2. 线程:开销小,切换更快。

  4. 独立性

    1. 进程:相互独立,一个进程崩溃不影响其他进程。

    2. 线程:共享进程资源,一个线程异常,可能导致整个进程崩溃。

  5. 通信

    1. 进程间通信复杂;线程间共享数据,通信简单。

多线程的应用场景 同时可以执行多个不同的命令,同时做多个不同的事情

软件中的耗时操作 所有的聊天软件 所有的后台服务器

拷贝大文件 加载大量的资源文件

并发和并行

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

并行:在同一时刻,有多个指令在多个CPU上同时执行

2、多线程的实现方式

多线程的实现方式

(1)继承Thread类的方式进行实现

(2)实现Runnable接口的方式进行实现

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

(1)第一种实现方式

Thread类其实就是一个线程

创建一个新线程方式:继承Thread类,该子类重写Thread类中的run方法。

/* '多线程的第一种启动方式: 1、自己定义一个类继承Thread 2、重写run方法 3、创建子类的对象,并启动线程 */

package Thread;

public class ThreadDemo1 {
    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();
    }
}
public class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            System.out.println(getName()+"HelloWorld");
        }
    }
}

(2)第二种实现方式

声明实现Runnable接口的类。实现run方法

/* 多线程的第二种启动方式: 1、自己定义一个类实现Runnable接口 2、重写里面的run方法 3、创建自己的类的对象 4、创建一个Thread类的对象,并开启线程 */

package Runnable;

public class RunnableDemo {
    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();
    }
}
public class MyRun implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            //获取到当前线程的对象
//            Thread t = Thread.currentThread();
//            System.out.println(t.getName() + "HelloWorld");
            System.out.println(Thread.currentThread().getName() + "HelloWorld");
        }
    }
}

(3)第三种实现方式

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

/* 多线程的第三种实现方式 特点:可以获取到线程运行的结果 1、创建一个类MyCallable实现Callable(带泛型<>)接口 2、重写call方法(是有返回值的,表示多线程运行的结果) 3、创建MyCallable的对象(表示多线程要执行的任务) 4、创建FutureTask<数据类型>的对象(作用管理多线程运行的结果) */

package CallableAndFuture;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableAndFutureDemo {
    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类的对象,并启动(表示多线程)
        Thread t1 = new Thread(ft);
        t1.start();
        //获取多线程运行的结果
        Integer result = ft.get();
        System.out.println(result);
    }
}
import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i=1;i<=100;i++){
            sum+=i;
        }
        return sum;
    }
}

(4)三种实现方式的对比及应用场景

如果使用第一种实现方式,因为java继承只能单继承,所以不能再继承其他类,扩展性差

如果想要得到线程结果,则使用第三种实现Callable和Future接口

3、多线程的常见成员方法

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

(1)简单方法

getName()和setName(String name)方法的细节:

1、如果我们没有给线程设置名字,线程也是有默认的名字的

   格式:Thread-X(X序号,从0开始的)

   2、如果我们要给线程设置名字,可以用set方法进行设置,也可以用构造方法设置

Static Thread currentThread() 获取当前线程的对象细节:

当JVM虚拟机启动后,会自动的启动多条线程

其中一条线程就叫做main线程

他的左作用就是去调用main方法,并执行里面的代码

在以前,我们写的所有的代码,其实都是运行在main线程当中

Static void sleep(long time) 让线程休眠指定的时间,单位为毫秒

package Threadmethod;

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        //构造方法设置线程名
        MyThread t1 = new MyThread("神人");
        MyThread t2 = new MyThread("人机");
        //setName设置线程名
        t1.setName("线程1");
        t2.setName("线程2");
        //开启线程
        t1.start();
        t2.start();
        Thread t = Thread.currentThread();
        System.out.println(t.getName());
        //sleep暂停当前线程5秒钟,然后继续执行代码
        System.out.println("111111111");
        Thread.sleep(5000);
        System.out.println("222222222");
    }
}
public class MyThread extends Thread {
    public MyThread() {
    }

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

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(getName() + "@" + i);
        }
    }
}

(2)线程的优先级

抢占式调度
  • setPriotity(int newPriority) 设置线程的优先级参数为1-10
  • Final int getPriority() 获取线程的优先级
注:优先级从1-10,默认是5,优先级越高抢占CPU概率
public class Demo2 {
    public static void main(String[] args) {
        //创建线程要执行的参数对象
        MyRun mr = new MyRun();
        //创建线程对象
        Thread t1 = new Thread(mr,"神人");
        Thread t2 = new Thread(mr,"人机");

        t1.setPriority(1);
        t2.setPriority(10);

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


    }
}
public class MyRun implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<=50;i++){
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}

(3)设置守护线程

  • Final void setDaemon(boolean on) 设置守护线程

细节:

当其他的非守护线程执行完毕之后,守护线程会陆续结束

通俗易懂:

  当女神线程执行完毕,备胎也没有存在的必要了,他们会陆陆续续去找其他女神,而并不是直接走掉

public class Demo3 {
    public static void main(String[] args) {
        MyThread1 t1 = new MyThread1();
        MyThread2 t2 = new MyThread2();

        t1.setName("女神");
        t2.setName("备胎");
    //把第二个线程设置为守护线程
        t2.setDaemon(true);

        t1.start();
        t2.start();
    }
}
public class MyThread1 extends Thread {
    @Override
    public void run() {
        for(int i=0;i<=10;i++){
            System.out.println(getName()+"---"+i);
        }
    }
}
public class MyThread2 extends Thread {
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            System.out.println(getName() + "---" + i);
        }
    }
}

(4)礼让线程

  • Public satic void yield() 出让线程/礼让线程

作用:让不同线程执行结果尽可能均匀

Thread.yield()

是一个静态方法,作用是主动让出当前 CPU 的执行权,让调度器重新选择线程执行。

注意:它只是 “礼貌地礼让”,不保证其他线程一定能抢到执行权,也不保证自己立刻就能被调度回来。

package com.itheima.a07threadmethod4;

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.setName("飞机");
        t2.setName("坦克");

        t1.start();
        t2.start();
    }
}
package com.itheima.a07threadmethod4;

public class MyThread extends Thread {
    @Override
    public void run() {
        // "飞机" "坦克"
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + "@" + i);
            // 表示出让当前CPU的执行权
            Thread.yield();
        }
    }
}

(5)插入线程

  • Public static void Join() 插入线程/插队线程
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        t.setName("土豆");
        t.start();

        // 表示把t这个线程,插入到当前线程之前。
        // t:土豆
        // 当前线程: main线程
        t.join();

        // 执行在main线程当中的
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程" + i);
        }
    }
}
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + "@" + i);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值