Java学习之多线程

线程的三种创建线程

  • Thread class: 继承实现线程类(不建议使用)
  • Runnable接口:实现接口(推荐使用,避免单继承局限性)
  • Callable接口: 实现接口(了解)

Thread Class 实现方式

1.创建一个类,用于继承Thread类,重写里面的run()方法。

2.创建线程对象,调用启动线程。

3.线程由cpu调度实现重写的run()方法。

public class Demo1 extends Thread{
    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("继承线程类,重写线程体:就是重写线程类里的run()方法");
        }
    }
}
public class Test1 {
    public static void main(String[] args) {
        //创建线程对象
        Demo1 demo1 = new Demo1();
        //启动线程
        demo1.start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("我是主方法");
        }
    }
}

普通方法调用和多线程调用

​ 线程执行时,主线程也会继续执行。

​ 线程开启不一定马上执行,由cpu调度安排。

实现多线程下载网图

1.安装commons-io.jar包,在项目下新建lib文件夹,拷贝到lib,右键包,选择Add a library.此时包就可以使用

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

public class WebDownload {
    public static void main(String[] args) {
        Image i1 = new Image("https://image.baidu.com/search/detail?","1");
        Image i2 = new Image("https://image.baidu.com/search/detail?" ,"2");
        Image i3 = new Image("https://image.baidu.com/search/detail?","3");

        i1.start();
        i2.start();
        i3.start();
    }
}

//图片信息类,实现线程继承
class Image extends Thread{
    private String url;
    private String name;

    @Override
    public void run() {
        Download download = new Download();
        download.down(url,name);
        System.out.println("下载了"+name);
    }

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

//创建一个下载器
class Download{
    public void down(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,方法出现问题");
        }
    }
}

代码逻辑:

  1. 首先创建了一个类完成下载器功能,调用commons-io.jar的文件工具类实现了用url下载网络图片的方法。
  2. 创建线程对象类,Image继承Thread类,定义了图片所需参数,重写run()方法,在方法体中创建了下载器对象,调用了下载的方法。
  3. 在启动类中创建多个线程对象,启动线程。

Runnable接口实现方式

声明Runnable接口实现类,实现run()方法,创建实现类实例,当成参数传递给线程并启动。

public class Demo1 implements Runnable{
    //实现run()方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("实现Runnable接口");
        }
    }

    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        //以下可以简化:new Thread(demo1).start();
        Thread thread = new Thread(demo1);//Thread中传入接口实现类对象是因为Thread中有这个构造方法
        thread.start();
    }
}

多个线程实现同种对象

案例:龟兔赛跑

/**
 * 多线程实现同种对象
 * 线程同步问题,数据紊乱。
 */
public class Test1 implements Runnable{
    int num =10;//票数
    @Override
    public void run() {
        while (true){
            if(num<=0)break;
          //模拟网络延时可以方法问题的发生性
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了第"+num--+"张票");
        }
    }
    public static void main(String[] args) {
        Test1 test = new Test1();
        new Thread(test,"小红").start();
        new Thread(test,"老师").start();
        new Thread(test,"黄牛").start();

    }
}

实现静态代理与Thread线程对比

//静态代理也需要真实对象,但是通过代理可以实现更多的事:婚庆案例

Lamda表达式

增强for遍历:

//格式:for(元素的数据类型 变量名:数组或者集合)
for(String s:list){
  sout(s);
}
//将数组list里的元素一个个取值出来赋给s
//idea快捷键:数组名.for

Lambda表达式的作用:简化匿名内部类,但并不是所有的匿名内部类都可以简化,只有函数式匿名内部类可以

/**
 * 函数式编程思想:忽略面向对象的复杂语法,强调做什么,而不是谁去做。强调方法体,而不是对象
 * Lambda表达式格式
 * ()-> {}
 * ():对应着方法的形参
 * ->:固定格式
 * {}:对应着方法的方法体
 */
public class Demo1 {
    public static void main(String[] args) {
        //匿名内部类
        method(new Swing(){
            @Override
            public void swimming(){
                System.out.println("游泳");
            }
        });
        //简化
        method(()->{
            System.out.println("继续游泳");
        });
    }

    private static void method(Swing s) {
        s.swimming();
    }
}
interface Swing{
    public abstract void swimming();
}

对于函数式接口,可以通过lambda表达式来创建该接口的对象

/**
 * 推导Lambda表达式
 */
public class LambdaTest1 {
    //3.静态内部类
    static class FunTrue2 implements Fun{
        @Override
        public void function1() {
            System.out.println("I Love Lambda~~~");
        }
    }

    public static void main(String[] args) {
        //实现类的实现方式
        new FunTrue().function1();
        //静态内部类的实现方式
        new FunTrue2().function1();

        //4.局部内部类
        class FunTrue3 implements Fun{
            @Override
            public void function1() {
                System.out.println("I Love Lambda!!!");
            }
        }
        //局部内部类的实现方式
        new FunTrue3().function1();

        //5.匿名内部类
        new Fun(){
            @Override
            public void function1() {
                System.out.println("I Love ...");
            }
        }.function1();
        
        //用Lambda简化:表达式只能简化函数式接口的匿名内部类写法
        Fun like = () -> {
            System.out.println("Lambda");
        };
        like.function1();
    }
}
//1.定义一个函数式接口
interface Fun{
    void function1();
}
//2.实现类
class FunTrue implements Fun{
    @Override
    public void function1() {
        System.out.println("I Love Lambda!");
    }
}
  

线程的生命周期

线程停止建议使用自定义标识位,不要使用线程类的关闭。

Start()是让线程进入就绪态,而不是调度。

new线程是创建线程。

线程休眠:sleep();每个线程都有一个锁,sleep不会释放锁。

线程礼让:不一定成功。让当前正在执行的线程暂停,而不是阻塞。yield();

线程强制执行:Join方法。可以想象成插队。

守护线程与普通线程

线程同步

多个线程操作同一个资源:并发

线程池

实现重复利用,便于线程管理。

posted @ 2023-02-25 18:18  安静的美美  阅读(17)  评论(0编辑  收藏  举报