初始多线程

多线程

1.线程简介

程序、进程、线程

程序:指令和数据的有序集合,静态概念

进程:执行程序的一次执行过程,动态概念

一个进程中可以包含若干个线程

线程:进程中默认的线程main( )用户线程、GC守护线程

2.线程创建

2.1继承Thread类【重点】
  1. 自定义线程类继承Thread类
  2. 重写run( )方法,编写线程执行体
  3. 创建线程对象,调用start( )方法启动线程

【注意】:线程开启不一定立即执行,由cpu调度执行

java 复制代码
package thread;

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

    public static void main(String[] args) {
//        main线程,主线程

//        创建一个线程对象
        TestThread thread = new TestThread();
//        调用start( )方法开启线程
        thread.start();

        for (int i = 0; i < 200; i++) {
            System.out.println("主线程正在执行中....");
        }
    }
}

练习thread,实现多线程同步下载图片

java 复制代码
package com.yehuda.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TestThread02 extends Thread{
    private String url;
    private String name;
    public TestThread02(String url,String name){
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件:"+name);
    }

    public static void main(String[] args) {
        TestThread02 t1 = new TestThread02("https://mp-fb49de55f308fef4a804.cdn.bspapp.com/121.jpg","1.jpg");
        TestThread02 t2 = new TestThread02("https://mp-fb49de55f308fef4a804.cdn.bspapp.com/121.jpg","2.jpg");
        TestThread02 t3 = new TestThread02("https://mp-fb49de55f308fef4a804.cdn.bspapp.com/121.jpg","3.jpg");
        t1.start();
        t2.start();
        t3.start();
    }

    class WebDownloader{
        public void downloader(String url ,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO异常,downloader出现问题!");
            }
        }
    }
}

【构造方法】

2.2实现Runnable接口【重点*】
java 复制代码
package com.yehuda.thread;

//多个线程同时操作一个对象
public class TestThread04 implements Runnable {
    private int tickerNums = 10;

    @Override
    public void run() {
        while (true){
            if (tickerNums<=0){
                break;
            }

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"--->拿到了第"+ tickerNums-- +"张票!");

        }
    }

    public static void main(String[] args) {
        TestThread04 thread04 = new TestThread04();

        new Thread(thread04,"小明").start();
        new Thread(thread04,"老师").start();
        new Thread(thread04,"黄牛").start();
    }
}
//结果中会有两条线程抢同一张票

龟兔赛跑练习

java 复制代码
package com.yehuda.thread;

public class Race implements Runnable{
    private static  String winner;
    @Override
    public void run() {
        for (int i = 0; i <= 1000; 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>=1000){
            winner = Thread.currentThread().getName();
            System.out.println("winner is "+winner);
            return true;
        }else {
            return false;
        }
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}
2.3实现Callable接口【了解】
  • 可以定义返回值
  • 可以返回异常
java 复制代码
package com.yehuda.thread02;

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;


public class TestCallable implements Callable<Boolean> {

    private String url;
    private String name;
    public TestCallable(String url,String name){
        this.url = url;
        this.name = name;
    }
    @Override
    public Boolean call(){
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建对象
        TestCallable t1 = new TestCallable("https://mp-fb49de55-5118-456c-af62-f308fef4a804.cdn.bspapp.com/121.jpg","1.jpg");
        TestCallable t2 = new TestCallable("https://mp-fb49de55-5118-456c-af62-f308fef4a804.cdn.bspapp.com/121.jpg","2.jpg");
        TestCallable t3 = new TestCallable("https://mp-fb49de55-5118-456c-af62-f308fef4a804.cdn.bspapp.com/121.jpg","3.jpg");

//        1.创建执行服务
        ExecutorService executorService = Executors.newFixedThreadPool(3);
//        2.提交执行服务
        Future<Boolean> r1 = executorService.submit(t1);
        Future<Boolean> r2 = executorService.submit(t2);
        Future<Boolean> r3 = executorService.submit(t3);
//        3.获取结果
        boolean res1 = r1.get();
        boolean res2 = r2.get();
        boolean res3 = r3.get();
        System.out.println(res1);
        System.out.println(res2);
        System.out.println(res3);
//        关闭服务
        executorService.shutdownNow();
    }

    public class WebDownloader{
        public void downloader(String url ,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO异常,downloader出现问题!");
            }
        }
    }
}

3.静态代理

java 复制代码
package com.yehuda.staticPeoxy;

//静态代理模式总结:
//真实对象和代理对象都实现了同一个接口
//代理对象要代理真实对象
//好处:
    //代理对象可以做很多真实对象做不了的事情
    //真是对象专注做自己的事情
public class StaticProxy {
    public static void main(String[] args) {
        You you = new You();
        new WeddingCompany(you).HappyMarry();
//      new Thread(target).start();
    }
}

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() {
            before();
            this.target.HappyMarry();
            after();
        }

        private void before() {
            System.out.println("结婚前,布置现场!!");
        }

        private void after() {
            System.out.println("结婚之后,收尾款!!!");
        }
}

4.Lamda表达式

函数式接口:任何接口,只包含唯一一个抽象方法

对于函数式接口,我们可以通过lamda表达式来创建接口对象

4.1为什么要用lamda表达式
  • 避免匿名内部类定义过多
  • 让代码看起来简洁
  • 去掉没有意义的代码,留下核心的逻辑

一步一步简化过程

java 复制代码
package com.yehuda.lamda;

public class TestLamda {
//    3.静态内部类
    static class Like02 implements ILike{
        @Override
        public void lamda() {
            System.out.println("i like lamda2");
        }
    }

    public static void main(String[] args) {
        ILike like = new Like();
        like.lamda();

        like = new Like02();
        like.lamda();

//        4.局部内部类
        class Like3 implements ILike{
            @Override
            public void lamda() {
                System.out.println("i like lamda3");
            }
        }
        like = new Like3();
        like.lamda();

//        5.匿名内部类,没有类的名称,必须借助接口或则父类
        like = new ILike() {
            @Override
            public void lamda() {
                System.out.println("i like lamda4");
            }
        };
        like.lamda();

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

}

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

//2.实现类
class Like implements ILike{
    @Override
    public void lamda() {
        System.out.println("i like lamda1");
    }
}

Lamda表达式简化

  • lamda表达式,只有一行可以简化成一行代码,否则需要代码块包裹
  • 前提是接口为函数式接口,一个抽象函数
  • 多个参数也可以去掉参数类型,要么全去掉,要加括号,ILove love04 = (a,b,c)-> System.out.println(a+b+c);
java 复制代码
//lamda表达式简化
package com.yehuda.lamda;

public class TestLamda02 {


    public static void main(String[] args) {
        ILove love = (int a)-> {
            System.out.println(a);
        };
        love.love(520);

//        1.简化参数类型
        ILove love02 = (a)-> {
            System.out.println(a);
        };
        love02.love(521);
//        2.简化括号
        ILove love03 = a-> {
            System.out.println(a);
        };
        love03.love(522);
//        3.去掉花括号
        ILove love04 = a-> System.out.println(a);
        love04.love(523);
//        总结:
//            lamda表达式,只有一行可以简化成一行代码,否则需要代码块包裹
//            前提是接口为函数式接口,一个抽象函数
//            多个参数也可以去掉参数类型,要么全去掉,要加括号ILove love04 = (a,b,c)-> System.out.println(a+b+c);
    }
}

interface ILove{
    void love(int a);
}

5.线程状态

五个状态:新生、就绪、运行、阻塞、死亡

5.1线程停止
  • 不推荐使用 JDK 提供的stop( ) 、destroy( )方法
  • 推荐线程自己停下来,使用一个标志位进行终止变量,当flag=false,则终止线程运行
java 复制代码
package com.yehuda.state;

//测试STOP线程
//1.建议线程正常停止--->利用次数,不建议死循环
//2.建议使用标志位--->设置一个标志位
//3.不要使用stop或destory等过时或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==900){
//                调用stop方法,是线程停止
                testStop.stop();
                System.out.println("线程停止..");
            }
        }
        System.out.println("main线程停止...");
    }
}
5.2线程休眠
  • sleep(时间)指定当前线程阻塞的毫秒数;
  • sleep存在异常InterruptedException;
  • sleep时间达到后线程进入就绪状态;
  • sleep可以模拟网络延迟、倒计时等;
  • 每一个对象多有一个锁,sleep不会释放锁

模拟网络延时

java 复制代码
package com.yehuda.state;

import com.yehuda.thread01.TestThread04;

//模拟网络延时:放大问题的发生性
public class TestSleep implements Runnable{
    private int tickerNums = 10;

    @Override
    public void run() {
        while (true){
            if (tickerNums<=0){
                break;
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"--->拿到了第"+ tickerNums-- +"张票!");
        }
    }

    public static void main(String[] args) {
        TestThread04 thread04 = new TestThread04();

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

设置倒计时/打印当前时间

java 复制代码
package com.yehuda.state;

import java.text.SimpleDateFormat;
import java.util.Date;

//模拟倒计时
public class TestStop02 {
    public static void main(String[] args) {
//        倒计时
//        try {
//            tenDown();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

//        打印当前时间
//        获取当前系统时间
        Date date = new Date(System.currentTimeMillis());
        
        while (true){
            try {
                Thread.sleep(1000);
                //时间格式化
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
                date = new  Date(System.currentTimeMillis());//更新时间
                
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static void tenDown() throws InterruptedException {
        int ten = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(ten--);
            if (ten<=0){
                break;
            }
        }
    }
}
5.3线程礼让
  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转换为就绪状态
  • 让cpu重新调度,礼让不一定成功!看CPU调度
java 复制代码
package com.yehuda.state;

//线程礼让不一定成功
public class TestYield {
    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()+"线程停止....");
    }
}
5.4线程插队
  • 其他线程阻塞,执行插队线程
  • 一开始CPU调度main线程和VIP线程,VIP线程join插队后,CPU只调度VIP线程
java 复制代码
package com.yehuda.state;

public class TestJoin implements  Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("VIP线程插队来了"+i);
        }
    }

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

        for (int i = 0; i < 1000; i++) {
            System.out.println("main线程"+i);
            if (i == 200){
                thread.join();//VIP线程插队
            }
        }
    }
}
5.5线程状态
java 复制代码
package com.yehuda.state;

//观察测试线程状态
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("...............");
        });

        Thread.State state = thread.getState();
        System.out.println(state);

        thread.start();
        state = thread.getState();
        System.out.println(state); //RUN

        while (state != Thread.State.TERMINATED){
            Thread.sleep(1000);
            state = thread.getState();
            System.out.println(state);
        }
    }
}
5.6线程优先级
  • 先设置优先级再执行
  • 优先级低只意味着获得CPU调度的概率低
java 复制代码
package com.yehuda.state;

public class TestPriority {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread thread01 = new Thread(myPriority);
        Thread thread02 = new Thread(myPriority);
        Thread thread03 = new Thread(myPriority);
        Thread thread04 = new Thread(myPriority);

        thread01.start();

        thread02.setPriority(10);
        thread02.start();

        thread03.setPriority(7);
        thread03.start();

        thread04.setPriority(3);
        thread04.start();
    }
}
class MyPriority implements  Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}
5.7守护进程
  • 线程分为用户进程和守护进程
  • 虚拟机必需确保用户进程执行完毕
  • 虚拟机不必等待守护进程执行完毕
  • 守护进程,后台记录操作日志、监控日志、垃圾回收等待...
java 复制代码
package com.yehuda.state;

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);
        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("人活36500天,第"+i+"天!!");
        }
        System.out.println("goodBuy World!!!");
    }
}

6.线程同步

  • 解决线程安全性,Synchronized
  • 线程同步其实就是排队、一个等待机制
  • 多个需要同时访问此对象的线程进入这个对象的等待池
  • 保证安全,会损失性能
  • 优先级高的线程等待优先级低的线程释放锁,会引起性能导致问题

形成条件:队列+锁

同步方法:控制对"对象的访问",方法一旦执行,就独占锁synchronized默认锁的是this,及方法所在的方法

同步块:synchronized(Obj){},Obj为同步监视器,可以为任何对象

锁的对象要是增删改的对象

案例:

  • 演唱会卖票
  • 银行取钱
  • ArrayList-->CopyOnwriteArrayList(线程安全)

7.死锁

  • 多个线程各自占有部分共享资源,且互相等待其他线程释放资源才能运行,的停止执行的场景。【多个线程互相抱着对方需要的资源,然后形成僵持】

8.Lock锁

对比Synchronized

  • Lock是显示锁(手动开启和关闭),synchronized是隐式锁,出了作用域自动释放
  • lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM花较少时间来调度线程,性能更好,并具有更好的扩展性(提供更多的子类,ReentrantLock)
  • 使用顺序
    • Lock>同步代码块>同步方法

9.线程协作

生产者消费者模式(非设计模式)

多线程之间通信

  • 线程同步问题
  • 生产者和消费者共享一个资源
  • 两者互为依赖、互为条件

Producer---->数据缓存区----->Consumer

线程通信提供方法:

  • wait( ) :通知线程一直等待,直到其他线程通知,与sleep不同,会释放锁
  • notify( ) :唤醒一个处于等待状态的线程
  • 均是Object类的方法,都只能在同步方法和同步代码块中使用,否则抛出IIIegalMonitorStateException
  1. 管程法:利用缓存区解决

    • 生产者
      • 构造器(缓存区)
      • 重写run方法,生产产品
    • 消费者
    • 产品
      • id
    • 缓冲区
      • 容器大小,设置容器计数器
      • 生产者放入
        • 容器满了,通知消费者消费
        • 没有满
      • 消费者取出
        • 判断能否消费,没有则等待生产者生产
        • 如果可以则消费
    java 复制代码
    package com.yehuda.ProCen;
    
    //管程法
    public class TestPC {
        public static void main(String[] args) {
            SynContainer container = new SynContainer();
            new Producer(container).start();
            new Consumer(container).start();
        }
    }
    
    //生产者
    class Producer extends Thread{
        SynContainer container;
        public  Producer(SynContainer container){
            this.container = container;
        }
    //    生产
        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                container.push(new Chicken(i));
                System.out.println("生产了第"+i+"个鸡");
            }
        }
    }
    
    //消费者
    class  Consumer extends Thread{
        SynContainer container;
        public Consumer(SynContainer container){
            this.container = container;
        }
        @Override
        public void run() {
            for (int i = 1; i <= 100; i++) {
                System.out.println("消费了第"+container.pop().c_id+"只鸡");
            }
        }
    }
    
    class Chicken {
        public Chicken(int c_id) {
            this.c_id = c_id;
        }
    
        public int c_id;
    }
    
    class SynContainer {
    //    容器大小
        Chicken[] chickens = new Chicken[10];
    //    计数器
        int count = 0;
    
    //    生产者放入
        public synchronized void push(Chicken chicken){
            if (count == chickens.length){
    //            容器满了
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    //        容器美满
            chickens[count] = chicken;
            count++;
    //        通知消费者消费
            this.notifyAll();
        }
    
    //    消费者消费
    
        public synchronized Chicken pop(){
            if (count == 0){
    //            等待生产者生产
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
    //        消费
            Chicken chicken = chickens[--count];
            this.notifyAll();
            return chicken;
        }
    }
  2. 信号灯法:标志位法

java 复制代码
package com.yehuda.ProCen;

//信号灯法
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv  = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%3==0){
                this.tv.play("快乐大本营第"+i+"集");
            }else {
                this.tv.play("相亲大作战");
            }
        }
    }
}
class  Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv  = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

class TV{
    String voice;
    boolean flag = true;

    public synchronized void play(String voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("表演:"+voice);
        this.notifyAll();
        this.voice = voice;
        this.flag = !this.flag;
    }
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看:" + voice);
        this.notifyAll();
        this.flag = !this.flag;
    }
}

10.线程池

避免频繁创建、销毁线程

  • 提高响应速度
  • 降低资源消耗
  • 便于线程管理
  1. JDK5.0开始提供了线程池相关的API:ExecutorService和Executors
  2. void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable;Futuresubmit(Callabletask):执行任务,有返回值,一般又来执行Callable
  3. 关闭连接池
    • service.shutdownNow()
      • 调用shutdownNow()方法会立即关闭线程池,即停止所有正在执行的任务,并返回尚未执行的任务列表。
      • 该方法会尝试中断正在执行的任务,如果任务能够响应中断并正确处理,则任务会被中断,否则任务可能会继续执行。
      • 该方法返回一个List,包含尚未执行的任务列表。
    • service.shutdown()
      • 调用shutdown()方法会平缓地关闭线程池,即不再接受新的任务,但会等待已经提交的任务执行完毕。
      • 该方法不会中断正在执行的任务,而是等待它们执行完成。
      • 一旦所有任务执行完毕,线程池会被关闭
相关推荐
P.H. Infinity19 分钟前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天23 分钟前
java的threadlocal为何内存泄漏
java
caridle34 分钟前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^39 分钟前
数据库连接池的创建
java·开发语言·数据库
苹果醋343 分钟前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花1 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端1 小时前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan1 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer081 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源
全栈开发圈1 小时前
新书速览|Java网络爬虫精解与实践
java·开发语言·爬虫