JavaSE--多线程(基础篇)

1.多线程简述

Process 进程:是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。

Thread 线程:是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的 资源。虽然系统是把资源分给进程,但是CPU很特殊,是被分配到线程的,所以线程是CPU分配的基本单位。

简单来说:一个程序就是一个进程,而一个程序中的多个任务则被称为线程。

举例:电脑运行爱奇艺观看电影,此时计算机就会出现一个进程(爱奇艺),当我们播放电影的时候,这个进程中又会出现很多任务,这些任务也被称为线程。这也就造就了我们可以一边观看影像动画,一边听声音,一边看实时弹幕。

2.线程的创建

1.线程的三种创建方式

在这里插入图片描述

方式一:继承Thread类

----------创建线程------------
1.创建一个类继承Thread类
2.重写Thread类(父类)中的run方法

----------调用线程------------
3.实例化继承Thread类的类
4.调用该对象的start() 方法 开启线程

----------注意点---------------
1.线程对象.run() 方法会在主线程执行前执行
2.线程对象.start() 方法会另外开辟一个线程,
不影响主线程的执行,双向执行
3.线程开启不一定立即执行,是由CPU调度执行的
//线程创建方式一:继承Thread类 ,重写run()方法,调用start开启线程
public class ThreadOne extends Thread {
    //重写run方法
    public void run(){
        //run方法线程体
        for(int i = 1; i <= 50; i++){
            System.out.println("重写了run方法");
        }
    }
    
    //main线程 (主线程)
    public static void main(String[] args) {
        //创建线程对象
        ThreadOne threadOne = new ThreadOne();
        //调用线程对象的start()方法,开启线程
        threadOne.start();
		//主线程的方法
        for(int i = 1; i <= 50; i++){
            System.out.println("主线程中的方法");
        }
    }
}

方式二:实现Runnable接口

----------创建线程------------
1.创建一个类实现Runnable接口
2.实现Runnable类中的run()方法

----------调用线程------------
3.创建接口Runnable实现类对象
4.创建Thread对象,将Runnable实现类通过构造器的方式传入Thread对象中
5.调用Thead对象的start()方法
//实现Runnable接口 重写run方法
public class ThreadTwo implements Runnable{
    //重写run方法
    @Override
    public void run(){
        //run方法线程体
        for(int i = 1; i <= 200; i++){
            System.out.println("重写了run方法" + i);
        }
    }

    //main线程 (主线程)
    public static void main(String[] args) {
        //创建接口runnable实现类对象
        ThreadTwo threadTwo = new ThreadTwo();
        //创建Thread对象,通过构造器传入一个runnable实现类
        //调用start方法执行线程
        new Thread(threadTwo).start();


        for(int i = 1; i <= 2000; i++){
            System.out.println("主线程中的方法");
        }
    }
}

方式三:实现Callable接口(了解)

1.实现Callable接口需要有返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务ExecutorService ser = Executors.newFixedThreadPool(1);
5.提交执行Future<返回值类型> result = ser.submit(线程对象);
6.获取结果 返回值类型 r1 = result.get();
7.关闭服务 set.shutdownNow();
建议使用方式二(实现Runnable接口)为了避免OOP单继承局限性,
方便同一个对象被多个线程使用

Thread类常用方法

void start(): 启动线程,并执行对象的run()方法

run(): 线程在被调度时执行的操作

String getName(): 返回线程的名称

void setName(String name):设置该线程名称

tatic Thread currentThread(): 返回当前线程。在Thread子类中就
是this,通常用于主线程和Runnable实现类

static void yield(): : 线程让步
暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
若队列中没有同优先级的线程,忽略此方法

join() : : 当某个程序执行流中调用其他线程的 join() 方法时,
调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止
低优先级的线程也可以获得执行

static void sleep(long millis) :(指定时间:毫秒)
令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,
时间到后重排队。抛出InterruptedException异常

stop(): 强制线程生命期结束,不推荐使用

boolean isAlive(): : 返回boolean,判断线程是否还活着

注意点:

  1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
  2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU
    调度决定。
  3. 想要启动多线程,必须调用start方法。
  4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”

3.初识并发

模拟抢票
定义三个线程操作同一个对象
public class ThreadThree implements Runnable{

    //定义票数20张
    private int picketCount = 10;

    @Override
    public void run() {
        while(true){
            if(picketCount <= 0){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第" + picketCount-- +"张票");
        }
    }

    public static void main(String[] args) {
        ThreadThree threadThree = new ThreadThree();
        //三个线程操作同一个数据
        new Thread(threadThree,"线程(一)").start();
        new Thread(threadThree,"线程(二)").start();
        new Thread(threadThree,"线程(三)").start();
    }
}
上述程序运行结果:
线程(三)拿到了第10张票
线程(一)拿到了第9张票
线程(二)拿到了第8张票
线程(三)拿到了第7张票
线程(一)拿到了第6张票
线程(二)拿到了第5张票
线程(三)拿到了第4张票
线程(二)拿到了第2张票
线程(一)拿到了第3张票
线程(三)拿到了第1张票
线程(二)拿到了第-1张票
线程(一)拿到了第0张票

我们发现多个线程操作同一资源的情况下,线程是不安全的,数据紊乱

4.Lambda表达式

  • 函数式接口(Functional Interface) 是java8 Lambda表达式的关键
  • 函数式接口定义:
    • 任何接口,如果只包含唯一一个抽象方法,那么他就是函数式接口
      就比如这个接口 他只有一个抽象方法run
    public interface Runnable(){
     	public abstract void run();
     }
    
    • 对于函数式接口我们可以通过lambda表达式来创建对象

我们来实现一个函数式接口并创建对象的方法有

1. 通过普通类实现函数式接口,创建实现类对象
2. 通过静态内部类实现函数式接口,创建实现类对象
3. 通过局部内部类实现函数式接口,创建实现类对象
4. 通过匿名内部类,创建对象
5. 通过Lambda表达式创建对象
package com.it.thread;

/**
 * @author shkstart
 * @create 2020-05-14 21:21
 */

//lambda表达式
public class ThreadFive {

    // 3.静态内部类
    static class Like2 implements ILike{

        @Override
        public void lambda() {
            System.out.println("i like lambda 2");
        }
    }

    public static void main(String[] args) {

        ILike like = new Like(); // 普通实现类
        like.lambda();

        like = new Like2();  // 静态内部类
        like.lambda();


        // 4.局部内部类
        class Like3 implements ILike{

            @Override
            public void lambda() {
                System.out.println("i like lambda 3");
            }
        }

        like = new Like3(); // 局部内部类
        like.lambda();


        // 5.匿名内部类
        like = new ILike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda 4");
            }
        };

        like.lambda(); // 匿名内部类

        // 6. Lambda表达式
        like = () ->{
            System.out.println("i like lambda 5");
        };
        like.lambda(); // lambda表达式

    }


}

//1.定义函数式接口(有且只有一个抽象方法的接口)
interface ILike{
    void lambda();
}

// 2. 实现函数式接口
class Like implements ILike{

    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

我们为什么要使用Lambda表达式

为了避免匿名内部类定义过多
可以让代码看起来更简洁
去掉一堆没有意义的代码,只关注核心实现逻辑

Lambda表达式简化

public class ThreadSix {

    public static void main(String[] args) {
        ILove iLove = null;

        // lambda表达式简化
        iLove = (int a,int b) ->{
            System.out.println("a + b = " + (a + b));
        };

        // 1.简化参数类型
        iLove = (a,b) ->{
            System.out.println("a + b = " + (a + b));
        };

        // 2. 简化 参数类型 + 小括号(只有一个参数时才可以)
        /*ILove = a ->{
            System.out.println("xxx");
        };*/

        // 3. 简化 参数类型 + 小括号 + 大括号(只有一个参数且方法体只有一行代码才可以)
        // iLove = a -> System.out.println("xxx");

        iLove.love(10,20);
    }
}

interface ILove{
    void love(int a , int b );
}

5. 线程的状态

在这里插入图片描述
 JDK 中用Thread.State 类定义了 线程的几种 状态
要想实现多线程,必须在主线程中创建新的线程对象。Java语言使用Thread类
及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的 五种状态:

新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态

就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能

阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态

死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

线程的停止

不推荐使用JDK提供的 stop() 、destory()方法,【已废弃】

推荐让线程自己停下来

建议使用一个标志位来终止线程,例如 当flag = false时 线程停止
public class ThreadSeven implements Runnable {

    // 设定一个标志位,利用这个标志位控制线程的停止
    boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("i = " + i++);
        }
    }

    // 设置一个公开的方法停止线程,转换标志位
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        ThreadSeven threadSeven = new ThreadSeven();

        new Thread(threadSeven).start();
    
        // 当 i == 80000 的时候 调用stop方法停止线程
        for (int i = 0; i < 100000; i++) {
            if(i == 80000){
                threadSeven.stop();
                System.out.println("线程停止了");
            }
        }
    }
}

线程休眠_sleep()

在这里插入图片描述

// 线程休眠 sleep()
public class ThreadEight implements Runnable {

    // 实现倒计时
    @Override
    public void run() {
        int num = 10;
        while(true){
            System.out.println(num--);
            if(num == 0){
                break;
            }
            try {
                // 通过 sleep(1000)让程序休眠1000ms
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 获取当先系统时间并实时更新

        Date now = new Date(System.currentTimeMillis());

        while(true){
            System.out.println(new SimpleDateFormat("yy-MM-dd hh:mm:ss").format(now));
            Thread.sleep(1000);
            now = new Date(System.currentTimeMillis());
        }
    }
}

线程礼让_yield()

在这里插入图片描述

public class ThreadNine {
    public static void main(String[] args) {
        new Thread(new MyYield(),"m1").start();
        new Thread(new MyYield(),"m2").start();
    }
}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + ":线程开始执行");
        Thread.yield(); // 礼让其他线程
        System.out.println(Thread.currentThread().getName() + ":线程停止");
    }
}

线程强制执行_join()

//线程强制执行 .join()
public class ThreadTen implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("vip线程过来了" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new ThreadTen());
        thread.start();

        for (int i = 0; i < 500; i++) {
            System.out.println("主线程启动"+ i);
            if(i == 200){
                // 插队,主线程需要等待插队的线程执行完才可以执行
                thread.join();
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值