多线程详解---(多案例实战)

Posted ꧁༺空༒白༻꧂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程详解---(多案例实战)相关的知识,希望对你有一定的参考价值。

多线程

1、区分单线程和多线程

  • 单线程:就像是做饭,洗衣服,煮水,一个一个进行
  • 多线程:在单线程的基础上,可以考虑煮水的时候,洗衣服节约时间

在这里插入图片描述

package com.kong.thread;

//创建线程,重写run方法,start方法开启线程
public class thread1 extends Thread {
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i <1000 ; i++) {
            System.out.println("你好");
        }
    }

    public static void main(String[] args) {

        thread1 thread1=new thread1();//创建线程
        thread1.start();//start开启多线程
//        thread1.run();//如果是run方法,只是将run方法运行,并不是开启多线程

        for (int i = 0; i <1000 ; i++) {
            System.out.println(i);
        }
    }
}

测试结果:如果是run方法,就是按照你好输出完毕再输出i,如果是start(多线程)方法,则run方法跟i交替输出

2、网图下载

  • 首先我们要知道的是多线程的调度,是CPU在操作的,而代码执行过程中,下载的网图是先后顺序的,但是如果跟第一点写的煮水洗衣服,是不是煮水会先好,多线程也是如此,就是一个争取CPU调度的过程
  • 我们编写一段代码来试着探讨一下吧
package com.kong.thread;

import org.apache.commons.io.FileUtils;

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

//1.下载图片,需要一个下载的方法
//2.编写构造器
//3.new一个下载方法
//4.编写main方法下载图片
public class thread2 extends Thread {

    private String url;
    private String name;

    public thread2(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        DownPicture downPicture=new DownPicture();
        downPicture.down(url,name);
        System.out.println(name+"下载完毕");
    }

    public static void main(String[] args) {
        thread2 t1=new thread2("http://kr.shanghai-jiuxin.com/file/2021/0609/1af55d6d59a1624ff57f5f30e19eaa4d.jpg","逢考必过1");
        thread2 t2=new thread2("http://kr.shanghai-jiuxin.com/file/2021/0609/1af55d6d59a1624ff57f5f30e19eaa4d.jpg","逢考必过2");
        thread2 t3=new thread2("http://kr.shanghai-jiuxin.com/file/2021/0609/1af55d6d59a1624ff57f5f30e19eaa4d.jpg","逢考必过3");

        //开启多线程
        t1.start();
        t2.start();
        t3.start();
    }

}

//图片下载方法
class DownPicture{
    public void down(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 运行结果不难发现,三张相同的图片运行的顺序不一样,这是因为多线程的CPU调度,每个线程都在争取运行,导致相差不多的运行时间可以变化输出

3、Runable接口

启动线程方式:传入目标对象+Thread对象.start

package com.kong.thread;

public class runable implements Runnable {
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }

    public static void main(String[] args) {
        runable runable=new runable();//创建线程
        new Thread(runable).start();//开启线程

        for (int i = 0; i < 1000; i++) {
            System.out.println("ni");
        }
    }
}

在Thread类里面,继承了runable接口

4、并发问题

package com.kong.thread;

//模拟购票
public class runable2 implements Runnable {

    //定义一些票数
    private int tickNum=10;

    public void run() {
        while(true){
            if(tickNum<=1){
                break;
            }
            //模拟购票操作所需时间
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到第"+tickNum--+"票");
        }
    }

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

        new Thread(runable2,"小红").start();
        new Thread(runable2,"小明").start();
        new Thread(runable2,"小花").start();
    }
}

注意:在模拟购票所需时间之后会出现一种一起同时购买同一张票的情况(主要原因是你还在买票时间,有人也在买这张票)

5、龟兔赛跑问题

package com.kong.thread;

public class race implements Runnable {

    private String winner=null;

    public void run() {

    for (int i = 0; i <=100; i++) {
        //模拟兔子睡觉
                     if(Thread.currentThread().getName()=="兔子"&&i%10==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            boolean b = gameOver(i);
            if(b){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
        }
    }
    private boolean gameOver(int foot){
        if (winner!=null) {
            return true;
        }else if(foot>=100){
            winner=Thread.currentThread().getName();
            System.out.println(winner+"获得胜利");
            return true;
        }
        return false;
    }

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

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

6、静态代理模式

package com.kong.thread;

public class staticProxy {

    public static void main(String[] args) {
        weddingCompany wedding=new weddingCompany(new you());
        wedding.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}

//你去实现一个结婚的方法,此处的你为真实角色
class you implements Marry{
    public void HappyMarry() {
        System.out.println("你要结婚了");
    }
}

//结婚公司是作为代理角色
class weddingCompany implements Marry{

    private Marry target;

    public weddingCompany(Marry target){
        this.target=target;
    }
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }

    private void before(){
        System.out.println("婚前准备");
    }

    private void after(){
        System.out.println("婚后送客");
    }
}

7、Lambda表达式

函数式接口的定义:

  • 任何接口,如果只包含一个抽象方法,就称为函数式接口
  • 对于函数式接口,我们可以通过lambda表达式创建该接口对象
package com.kong.thread;

public class lambda{
    //静态内部类
//    static class Like implements ILike{
//        public void lambda() {
//            System.out.println("你好啊");
//        }
//    }

    public static void main(String[] args) {

        //局部内部类
//        class Like implements ILike{
//            public void lambda() {
//                System.out.println("你好啊");
//            }
//        }
//         ILike like=new Like();
//         like.lambda();

        //匿名内部类,只能借助父类或者接口
//        ILike like = new ILike() {
//            public void lambda() {
//                System.out.println("你好啊");
//            }
//        };
//         like.lambda();

        //lambda简化
        ILike like=()->{
            System.out.println("你好啊");
        };
        like.lambda();
    }
}

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

//实现类
//class Like implements ILike{
//    public void lambda() {
//        System.out.println("你好啊");
//    }
//}

总结

  1. 就是一个继承接口之后的简化
  2. 只适用于函数式接口

8、线程状态

  • 创建状态:new一个对象,线程对象一旦创建就进入新生状态
  • 就绪状态:当用start()方法式,线程立即进入就绪状态,但不一定马上调度
  • 阻塞状态:调用sleep,wait或者同步锁定,线程就会进入阻塞章台,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待cpu的调度
  • 执行状态:此处才是线程真正执行线程体的代码块
  • 死亡状态:线程中断或者结束,都会让线程进入死亡状态,并且不可再重新启动

在这里插入图片描述

8.1、线程停止

  • 建议线程正常停止—>利用次数,不建议死循环
  • 建议使用标志位
  • 不建议使用stop或者destroy
package com.kong.thread;

public class threadStop implements Runnable {

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

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

    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        threadStop threadStop=new threadStop();
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
            if(i==90){
                threadStop.stop();
                System.out.println("此线程停止");
            }
        }
    }
}

8.2、线程休眠

  • sleep(时间)指定当前线程阻塞的毫秒数
  • sleep存在异常InterruptedException
  • sleep时间到达后进程回进入就绪状态
  • sleep可以模拟网络延时,倒计时等
  • 每一个对象都有一个锁,sleep不会释放锁
package com.kong.thread;

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

public class timeSleep {

    public static void main(String[] args) {
        tenDown();
    }
    public static void tenDown() {
//        //倒计时
//        int num = 10;
//        while (true) {
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(num--);
//            if (num <= 0) {
//                break;
//            }
//        }
//    }
        //打印系统当前的事件
        Date date = new Date(System.currentTimeMillis());//获取当前系统时间
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            date = new Date(System.currentTimeMillis());
        }
    }

}

作用:放大事件可能发生的概率

8.3、线程礼让

  • 礼让线程,让当前正在执行的线程暂停,但是不阻塞
  • 运行转为就绪
  • cpu重新调度
package com.kong.thread;

public class threadYeid {
    public static void main(String[] args) {
        Myyied myyied = new Myyied();
        new Thread(myyied,"a").start();
        new Thread(myyied,"b").start();

    }
}
 class Myyied implements Runnable{

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

结论:礼让不一定能成功

8.4、线程强制执行join(插队)

package com.kong.thread;

public class threadjoin implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }

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

        for (int i = 0; i <50 ; i++) {
            if(i==20){
                thread.join();
            }
            System.out.println("停止");
        }
    }
}

8.5、线程状态观察

package com.kong.thread;

public class state {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            for (int i 多线程详解---(多案例实战)

python3多线程实战(python3经典编程案例)

python3多线程实战(python3经典编程案例)

Python应用实战-多线程应用案例

实战并发编程 - 09多线程Guarded Suspension模式案例实战

从入门到实战,Netty多线程篇案例集锦