Java基础08:多线程

2021/6/29 22:20:39

本文主要是介绍Java基础08:多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

线程

简介

  • Process(进程)与Thread(线程)
    1. 说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运 行的含义,是一个静态的概念。而进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位
    2. 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没 有存在的意义。线程是CPU调度和执行的的单位
    3. 注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务 器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能 执行一个代码,因为切换的很快,所以就有同时执行的错局。
  • 核心
    1. 线程就是独立的执行路径;
    2. 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
    3. main()称之为主线程,为系统的入口,用于执行整个程序;
    4. 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与 操作系统紧密相关的,先后顺序是不能认为的干预的。
    5. 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
    6. 线程会带来额外的开销,如cpu调度时间,并发控制开销。
    7. 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

线程实现

Thread

  1. 自定义线程类继承Tread类

  2. 重写run()方法,编写线程执行体

  3. 创建线程对象,调用start()方法启动线程

  4. 举例

    //创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
    //总结:线程开启不一定立即执行,由CUP调度执行
    public class TestThread1 extends Thread{
        @Override
        public void run() {
            //run方法线程体
            for (int i = 0; i < 2000; i++) {
                System.out.println("我在看代码...." + i);
            }
        }
        public static void main(String[] args) {
            //创建一个线程对象
            TestThread1 testThread1 = new TestThread1();
            //调用start()方法开启线程
            testThread1.start();
            //main线程,主线程
            for (int i = 0; i < 2000; i++) {
                System.out.println("我在学习多线程" + i);
            }
        }
    }
    

Runnable

  1. 定义MyRunnable类实现Runnable接口

  2. 实现run()方法,编写线程执行体

  3. 创建线程对象,调用start()方法启动线程

    //
    public class TreadRunn implements Runnable {
        @Override
        public void run() {
            //run方法线程体
            for (int i = 0; i < 200; i++) {
                System.out.println("我在看代码。。。" + i );
            }
        }
    
        public static void main(String[] args) {
            //创建runnable接口的实现类对象
            TreadRunn treadRunn = new TreadRunn();
            //创建线程对象,通过线程对象来开启我们的线程,代理
    //        Thread thread = new Thread(treadRunn);
    //        thread.start();
    
            new Thread(treadRunn).start();
    
            for (int i = 0; i < 1000; i++) {
                System.out.println("我在学习多线程..." + i);
            }
    
        }
    

小结

  • 继承Thread类
    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start()
    • 不建议使用:避免OOP单继承局限性
  • 实现Runnable接口
    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象 + Tread对象.start()
    • 推荐使用:便面单继承局限性,灵活方便,方便同一个对象对多个线程使用

初识并发问题

  1. 发现问题

    //多个线程同时操作同一个对象
    //买火车票的例子
    
    //发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
    public class TestThread4 implements   Runnable{
    
        //票数
        private int ticketNums = 10;
        @Override
        public void run() {
            while(true){
                if (ticketNums <=0){
                    break;
                }
                //模拟延时
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "票");
            }
        }
    
        public static void main(String[] args) {
            TestThread4 ticket = new TestThread4();
    
            new Thread(ticket,"小明").start();
            new Thread(ticket,"老师").start();
            new Thread(ticket,"黄牛").start();
        }
    }
    
  2. 龟兔赛跑

    public class Race 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(10);
                    } 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;
            }{
                if (steps >= 100){
                    winner = Thread.currentThread().getName();
                    System.out.println("winner is " + winner);
                    return true;
                }
            }
            return false;
        }
    
        public static void main(String[] args) {
            Race race = new Race();
            new Thread(race,"兔子").start();
            new Thread(race,"乌龟").start();
        }
    
    }
    

Callable

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务,ExecutorService ser = Exectors.newFixedThreadPool(1);
  5. 提交执行:Future result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();

callable好处

  1. 可以定义返回值
  2. 可以抛出异常

Lambda表达式

  1. 为什么使用Lambda表达式

    • 避免匿名内部类定义过多
    • 可以让你的代码看起来很简洁
    • 去掉了一堆没有意义的代码,只留下核心的逻辑
  2. 理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在

    函数式接口的定义:

    • 任何接口,如果只包含唯一一个抽象方法,那么他就是以恶个函数式接口
    • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
  3. 其实质属于函数式编程的概念

  • (params) -->expression[表达式]
  • (params) -->statement[语句]
  • (params) -->{statements}
  1. 语句

    new Thread (() ->System.out.println("多线程学习....")).start
    
  2. 举例

    public class TestLambda {
        //3.静态内部类
        static class Like2 implements ILike{
    
            @Override
            public void lambda() {
                System.out.println(" i like lambda2");
            }
        }
    
        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 lambda3");
                }
            }
            like = new Like3();
            like.lambda();
    
            //5.匿名内部类:没有内部类,必须借助接口或者父类
            like = new ILike() {
                @Override
                public void lambda() {
                    System.out.println(" i like lambda4");
                }
            };
            like.lambda();
    
            //6.用lambda简化
            like = () -> {
                System.out.println(" i like lambda5");
            };
            like.lambda();
        }
    }
    //1.定义一个函数式接口
    interface ILike{
        void lambda();
    }
    //2.实现类
    class Like implements ILike{
    
        @Override
        public void lambda() {
            System.out.println(" i like lambda");
        }
    }
    

    举例2

    public class TestLambda2 {
        //静态内部类
        static class Love2 implements ILove{
            @Override
            public void love(int a) {
                System.out.println(" i love you ----》" + a);
            }
        }
    
    
        public static void main(String[] args) {
            ILove  love = new Love();
            love.love(1);
    
            love = new Love2();
            love.love(2);
    
            //局部内部类
            class Love3 implements ILove{
                @Override
                public void love(int a) {
                    System.out.println(" i love you ----》" + a);
                }
            }
            love = new Love3();
            love.love(3);
    
            //匿名内部类
            love = new ILove() {
                @Override
                public void love(int a) {
                    System.out.println(" i love you ----》" + a);
                }
            };
            love.love(4);
    
            love = (int a) ->{
                System.out.println(" i love you ----》" + a);
            };
            love.love(520);
    
            //简化1.参数类型
            love = (a) ->{
                System.out.println(" i love you ----》" + a);
            };
            love.love(521);
            //简化2.简化括号
            love = a ->{
                System.out.println(" i love you ----》" + a);
            };
            love.love(5211);
            //简化3.去掉花括号
            love = a -> System.out.println(" i love you ----》" + a);
            love.love(5212);
    
            /*
            总结:
                lambda表达式只能有一行代码的情况下才能简化成一行
                前提是必须是函数式接口
             */
        }
    
    }
    interface ILove{
        void love(int a);
    }
    class Love implements ILove{
        @Override
        public void love(int a) {
            System.out.println(" i love you ----》" + a);
        }
    }
    
  3. 简化总结

    • lambda表达式只能有一行代码的情况下才能简化成一行
    • 前提是必须是函数式接口
    • 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

线程状态

线程方法

线程停止

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

  2. 推荐线程自己停止下来

  3. 建议使用一个标志位进行终止变量当flag = false,则终止线程运行

  4. 举例

    //测试stop
    //1.建议线程正常停止---》利用次数,不建议死循环
    //2.建议使用标志位---》设置一个标志位
    //3.不要使用stop或者destroy等过时或者JDK不建议使用的方法
    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 == 500){
                    //调用stop方法,切换标志位,停止线程
                    testStop.stop();
                    System.out.println("线程停止");
                }
            }
        }
    }
    

线程休眠

  1. sleep(时间) 指定当前线程阻塞的毫秒数 【1000毫秒 = 1秒】

  2. sleep存在异常InterrupturedException;

  3. sleep时间达到后线程进入就绪状态

  4. sleep可以模拟网络延时、倒计时等

  5. 每一个对象都有一个锁,sleep不会释放锁

    package com.tread.demo01.state;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class TestSleep2 {
        public static void main(String[] args) {
           /* try {
                tenDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
    
            //打印当前系统时间
            Date startTime = new Date(System.currentTimeMillis());//获取当前时间
    
            while(true){
                try {
                    Thread.sleep(1000);
                    System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                    startTime = new Date(System.currentTimeMillis());//更新当前时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        //模拟倒计时
        public static void tenDown() throws InterruptedException {
            int num = 10;
    
            while (true){
                Thread.sleep(1000);
                System.out.println(num--);
                if (num <=0 ){
                    break;
                }
            }
        }
    }
    
    

线程同步

线程通信问题



这篇关于Java基础08:多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程