多线程(一)

多线程(一)


多任务: 边吃饭边看手机、边走路边听歌、边上wc边玩手机

​ 这些看起来Q是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件事;

多线程: 原来只有 1 条道路,慢慢车多了,道路堵塞,通过效率极低;为了提高效率,在道路上加了多个车道,提高效率;

​ 打王者,原来只有一个账号,后来开通了多个账号,大家可以一起玩;

在这里插入图片描述

进程与线程:

  					1. 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念;
  					2. 进程 是执行程序的一次执行过程,他是一个动态的概念,是系统资源分配的单位;
  					3. 通常在一个进程中可以包含若干个 线程 ,(一个进程中 至少 包含 一个线程,不然没有存在的意义),线程是调度和执行的单位

注意:很多线程是模拟出来的,真正的多线程是指有多个cpu,即多核 ,如服务器 。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉;


01 线程创建

Thread、Runnable、Callable


三种创建方式

Thread class ===> 继承 Thread 类 (重点)

Runnable 接口 ===> 实现 Runnable 接口 (重点)

callable 接口 ====> 实现 Callable 接口 (了解)


继承 Thread 类

步骤:

  1. 自定义线程继承 Thread 类
  2. 重写 run() 方法,编写线程执行体
  3. 创建线程对象,调用 start() 方法启动线程
//创建线程方式一:继承 Thread 类,重写 run()方法,调用 start 开启线程
public class TestThread extends Thread{

    @Override
    public void run() {
        //run 方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("多线程run方法执行!!!!");
        }

    }

    //main线程,主线程
    public static void main(String[] args) {
        //创建一个线程对象
        TestThread testThread =new TestThread();

        //调用start()方法开启线程
        testThread.start();

        for (int i = 0; i < 200; i++) {
            System.out.println("这是main方法线程!!!!!!!");
        }

    }
}

总结:线程开启不一定立即执行,是由 cpu 来调度安排执行


实现 Runnable 接口

步骤:

  1. 定义 MyRunnable 类实现 Runnable 接口
  2. 实现 run() 方法,编写线程执行体
  3. 创建线程对象,调用 start() 方法启动线程
//创建线程方式2 ;实现 runnablle 接口,重写 run方法,执行线程需要当如 runnable 实现类
public class TestThread2 implements Runnable {
    //run方法线程体
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("这是run方法!!!!" + i);
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread2 testThread2 = new TestThread2();

        //创建线程对象,通过线程对象来开启线程;代理模式(静态代理)
//        Thread thread=new Thread(testThread2);
//        thread.start();

        new Thread(testThread2).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("这是 main 方法!!!!" + i);
        }
    }
}

注意:因为Java是单继承模式,推荐使用 runnable 接口实现线程

两者对比:

  • 继承 Thread 类

    1. 子类继承 Thread 类
      2. 启动:子类对象 . start()
      3. 不建议;避免 oop 单继承局限性
  • 实现 Runnable 接口

    1. 实现Runnable 接口
      2. 启动:传入目标对象(runnable接口实现类)+ Thread对象 . start()
      3. 推荐:避免单继承局限性,方便同一个对象被多个线程使用,灵活;

推荐runnable 接口原因:

Java是单继承,一个子类只能有一个父类;加入有a、b两个类,且a、b两个类相互之间没有关联,现在有一个c类,想同时继承a、b类,是无法实现的;

Java中一个类可以实现多个接口,所以上面这种情况,如果是接口,那么 c 就可以同时实现 a、b 类;所以多线程推荐使用实现 Runnable 接口方式


案例(龟兔赛跑)

//多线程同时操作同一个对象;龟兔赛跑
public class TestThread3 implements Runnable {
    private static String winner;

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            //让乌龟获胜
            if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断是否结束比赛
            boolean flag = gameOver(i);
            if (flag) {
                break;
            }
            System.out.println(Thread.currentThread().getName() + "===>跑了" + i + "步");
        }
    }

    //判断是否完成比赛
    private boolean gameOver(int steps) {
        if (winner != null) {
            //已经存在胜利者
            return true;
        } else {
            if (steps >= 100) {
                winner = Thread.currentThread().getName();
                System.out.println("冠军是: " + winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        TestThread3 testThread3 = new TestThread3();

        new Thread(testThread3, "兔子").start();
        new Thread(testThread3, "乌龟").start();
    }
}

实现 Callable 接口

步骤:

  1. 实现 callable 接口,需要返回值类型;
  2. 重写 call 方法 ,需要抛出异常;
  3. 创建目标对象;
  4. 创建执行服务:Executor Service ser = Executor . newFixedThreadPool (1);
  5. 提交执行:Future<Boolean> result1 =ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();
//线程创建方式三:实现 callable 接口
public class TestCallable implements Callable<Boolean> {
    private String name;

    public TestCallable(String name) {
        this.name = name;
    }

    @Override
    public Boolean call() {
        for (int i = 0; i < 20; i++) {
            System.out.println("call方法执行!!!!" + name);
        }
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("t1");
        TestCallable t2 = new TestCallable("t2");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);

        //获取结果
        boolean res1 = r1.get();
        boolean res2 = r2.get();
        //关闭服务
        ser.shutdownNow();
    }
}

02 静态代理

静态代理可以理解就是一种 委托 的模式;比如你需要结婚了,你(真实角色)委托婚庆公司 布置一些事情;

你自己 和 婚庆公司都需要做的事情 :你 结婚 ---- 你(真实角色)与 新娘结婚;婚庆公司 结婚----接受你的委托,布置一些场地事宜;

二者都需要同时做一件事情(实现同一个接口) ==== “结婚”;

/**
 * 静态代理:
 * 1.真实对象和代理对象都要同时实现同一个接口
 * 2.代理对象必须要代理真实角色
 *
 * 好处:
 * 1.代理对象可以做很多其他的辅助事情
 * 2.真实对象可以专注做自己的事情
 */
public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany=new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}

interface Marry {
    void HappyMarry();
}

//真实角色,你去结婚
class You implements Marry {
    @Override
    public void HappyMarry() {
        System.out.println("我很开心!要结婚了!");
    }
}

//代理角色,帮助你结婚
class WeddingCompany implements Marry{
    //代理===》真实目标
    private Marry target;

    public WeddingCompany(Marry target){
        this.target=target;
    }

    @Override
    public void HappyMarry() {
        after();
        this.target.HappyMarry();//真实对象
        befor();
    }
    private void after(){
        System.out.println("结婚之前,婚庆公司布置现场;");
    }
    private void befor(){
        System.out.println("结婚之后,婚庆公司收尾款,打扫现场;");
    }
}

03 Lambda 表达式

1.Lambda 简介

  1. λ希腊字母表中排序第十一位的字母,英文名:Lambda
  2. 避免匿名内部类定义过多的情况
  3. 属于函数式编程的概念
  4. JDK8 的一个新特性,可以取代大部分的匿名内部类,可以极大的优化代码结构,尤其是在集合遍历等情况下
  5. Lambda 规定接口中只能有一个需要被实现的方法;并不是接口中只能有一个方法

语法形式: () -> {}

‘ () ’ :描述参数列表;

“ { } ” :描述方法体 ;

“ -> ” : lambda 运算符

语法简化形式:

2. 函数式接口(Functional Interface)

函数式接口是学习 Lambda 表达式的关键所在

函数式接口定义:

  //任何接口 ,如果只包含唯一一个抽象方法,那么他就是一个函数式接口
    public interface Runnable{
        public abstract void run();
    }

3.案例:

/**
 * 推导lambda表达式
 */
public class TestLambda {
    //3.静态内部类
    static class Like2 implements ILike {
        @Override
        public void lambda() {
            System.out.println("这是 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("这是 lambda 3 局部内部类");
            }
        }
        like = new Like3();
        like.lambda();

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

        //6. 用 lambda 简化
        like = () -> {
            System.out.println("这是 lambda 5 用 lambda 简化");
        };
        like.lambda();
    }
}

//1.定义一个函数式接口
interface ILike {
    void lambda();
}

//2.实现类
class Like implements ILike {
    @Override
    public void lambda() {
        System.out.println("这是 lambda 实现类");
    }
}
这是 lambda 实现类
这是 lambda 2 内部类
这是 lambda 3 局部内部类
这是 lambda 4 匿名内部类
这是 lambda 5 用 lambda 简化

继续简化

        // 简化
        like = (int a) -> {
            System.out.println("这是 lambda 实现类" + a);
        };
        like.lambda(1);

        // 简化1  参数类型
        like = (a) -> {
            System.out.println("这是 lambda 实现类" + a);
        };
        like.lambda(2);

        // 简化2  括号
        like = a -> {
            System.out.println("这是 lambda 实现类" + a);
        };
        like.lambda(3);

        // 简化3  花括号
        like = a -> System.out.println("这是 lambda 实现类" + a);
        like.lambda(4);

简化总结:

    /**
     *    简化到最后:
     *    lambda 只能有一行代码的情况下才能简化成一行,如果多行,必须使用代码酷爱包裹
     *    前提 接口是函数式接口
     *    多个参数也可以去掉 参数类型 ,但是要去掉就要所有的都去掉,用括号包裹
     */

04 线程方法

setPriorith(int newPriority)  --更改线程的优先级
static void sleep(long millis) --在指定的毫秒数内让当前正在执行的线程休眠
void join() --等待线程终止
static void yield() --暂停当前正在执行的线程对象,并执行其他线程
void interrupt() --中断线程,别用这个方式
boolean isAlive() --测试线程是否处于活动状态

05 线程停止

  • 推荐线程自己停止下来,不推荐使用jdk提供的stop()、destroy()方法 【已废弃】
  • 建议使用一个标志位进行终止变量;当 flag = false ,则终止线程运行
public class TestStop implements Runnable{

    //1.设置一个标识位
    private boolean flag=true;

    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("run....Thread "+i++);
        }
    }
    //2.这只一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        TestStop testStop=new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main........"+ i);
            if (i==900){
                //调用stop方法切换标志位,让线程停止
                testStop.stop();
                System.out.println("线程停止了!");
            }
        }
    }
}

main........897
main........898
main........899
main........900
线程停止了!
main........901
main........902
main........903
main........904
main........905
main........906
main........907

06 线程休眠

sleep(时间) 指定当前线程阻塞的毫秒数;
sleep存在异常InterruptedException;
sleep时间到达后线程进入就绪状态;
sleep可以模拟网络延时,倒计时等;
每一个对象都有一个锁,sleep不会释放锁;
//模拟网络延时:放大问题的发生性
class TstSleep2 implements Runnable {
    //票数
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true) {
            if (ticketNums <= 0) {
                break;
            }
            //模拟网络延时
            try {
                Thread.sleep(100);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "=====>>拿到了第" + ticketNums-- + "张飘");
        }
    }


    public static void main(String[] args) {
        TstSleep2 tstSleep2=new TstSleep2();

        new Thread(tstSleep2,"小明").start();
        new Thread(tstSleep2,"老师").start();
        new Thread(tstSleep2,"黄牛党").start();
    }
}

07 线程礼让 - yield

  • 礼让线程,让当前正在执行的线程 暂停 ,但不阻塞;
  • 将线程从运行状态 转为 就绪状态;
  • 让 cpu 重新调度,礼让不一定成功!看cpu心情;
//线程礼让
public class TestyYeld {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield, "a").start();
        new Thread(myYield, "b").start();
    }
}

class MyYield implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程开始执行!");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName() + "线程停止执行!");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9fLy2Dh-1606114135918)(G:\笔记\Java-狂神-重新开始\多线程\image\image-20201116170130626.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KsBKmzQA-1606114135920)(G:\笔记\Java-狂神-重新开始\多线程\image\image-20201116170410933.png)]


08 线程强制执行- join

  • Join 合并线程,等词线程执行完成后,再执行其他线程,其他线程阻塞;
  • 类似插队;
//Join 插队
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("线程vip来了===" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //vip线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                thread.join();//插队
            }
            System.out.println("main" + i);
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z15BEIst-1606114135921)(G:\笔记\Java-狂神-重新开始\多线程\image\join)]


09 线程状态观测 - State

  • 线程状态。线程可以处于以下状态之一:

    • NEW
      尚未启动的线程处于此状态。
    • RUNNABLE
      在Java虚拟机中执行的线程处于此状态。
    • BLOCKED
      被阻塞等待监视器锁定的线程处于此状态。
    • WAITING
      正在等待另一个线程执行特定动作的线程处于此状态。
    • TIMED_WAITING
      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
    • TERMINATED
      已退出的线程处于此状态。

    一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

    public class TestState {
        public static void main(String[] args) {
            Thread thread = new Thread(() -> {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("//");
            });
            //观测状态
            Thread.State state = thread.getState();
            System.out.println(state);//new
    
    
            //观测启动后
            thread.start();
            state = thread.getState();
            System.out.println(state);//run
    
            while (state != Thread.State.TERMINATED) {//只要线程不终止,就一直输出状态
                state = thread.getState();//跟新线程状态
                System.out.println(state);
            }
        }
    
    }
    
    

10 线程优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行;
  • 线程的优先级用数字表示,范围从1-10 :
    • Thread.MIN_PRIOROTY =1 ;
    • Thread.MAX_PRIOROTY =10 ;
    • Thread.NORM_PRIOROTY=5 ;
  • 使用一下方式改变或者获取优先级
    • getPriority().setPriority(int xx)
//测试线程的优先级
public class TestPrioty {
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"---->>" +Thread.currentThread().getPriority());

        MyPriority myPriority =new MyPriority();

        Thread t1=new Thread(myPriority);
        Thread t2=new Thread(myPriority);
        Thread t3=new Thread(myPriority);
        Thread t4=new Thread(myPriority);
        Thread t5=new Thread(myPriority);
        Thread t6=new Thread(myPriority);

        //设置线程优先级,
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
        t4.start();

        t5.setPriority(8);
        t5.start();

        t6.setPriority(7);
        t6.start();

    }


}

class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->>"+Thread.currentThread().getPriority());
    }
}

注意:

  • 优先级的设定建议在 start()调度前;
  • 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看 cpu 的调度;

11 守护线程(daemon)

  • 线程分为 用户现场 和 守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 比如: 后台记录操作日志,监控内存,垃圾回收等待…
package com.lambda;

//测试守护线程
//上帝守护你
public class TestDaemon {
    public static void main(String[] args) {
        God god=new God();
        You you=new You();

        Thread thread=new Thread(god);
        thread.setDaemon(true);//默认是false表示用户线程,正常的线程都是用户数据...

        thread.start();//上帝守护线程

        new Thread(you).start();//你(用户线程)启动了...

    }

}

//上帝
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("上帝保佑着你!");
        }
    }
}


//你
class You implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你一直开心的活着!");
        }
        System.out.println("++++++++++++++goodble!world!+++++++++++++++++++++");
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值