java多线程基础

多线程实现方法

第一种方法:Thread

  • 自定义线程类继承Thread类

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

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

  • 注意:线程不一定立即执行,由CPU安排调度

继承Thread类创建多线程

package com.waves.dxcdemo;
​
import java.text.DateFormat;
import java.time.format.DateTimeFormatter;
import java.util.Date;
​
//创建多线程方式一:继承Thread类,重写run()方法,调用start开启线程
//总结 线程开启子线程不一定立即执行 ,由cpu调度执行
public class TestThread1 extends Thread{
    @Override
    public void run() {
        //线程方法体
        for (int i = 0; i < 20; i++) {
            System.out.println("2021-11-28 18:22 多线程代码"+i);
        }
    }
​
    public static void main(String[] args) {
        //main方式,主线程
        TestThread1 thread1 = new TestThread1();
        thread1.start();  //调用start方法,run 与 下方学习代码交替执行
// thread1.run();  //调用run方法,优先执行run方法体的业务代码
//        Date date = new Date();
//        long time = date.getTime();
for (int i = 0; i < 20; i++) {
            System.out.println("2021-11-28 18:22 学习多线程代码"+ i);
        }
    }
}

 

继承Thread类使用多线程同步下载图片

package com.waves.dxcdemo;
​
​
import org.apache.commons.io.FileUtils;
​
import java.io.File;
import java.io.IOException;
import java.net.URL;
​
//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
​
    private String url; //下载图片的地址
    private String name; //要下载(保存)的图片名字哦
public TestThread2(String url,String name){
        this.url = url;
        this.name =name;
    }
​
    @Override
    public void run() {
        WedDownLoader wedDownLoader = new WedDownLoader();
        wedDownLoader.downLoader(url,name);
        //图片保存在根目录
        System.out.println("下载图片路径为"+url+"\n"+"下载的图片名字为"+name);
    }
​
    public static void main(String[] args) {
        TestThread2 xiancheng1 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fd.zdqx.com%2Fshirt_20190710%2F014.jpg&refer=http%3A%2F%2Fd.zdqx.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641202820&t=637cea2d77c04500bdbef46abce0e64e","动漫桌面1.jpg");
        TestThread2 xiancheng2 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fba794af4e3c6a4cf93b25f19e148dc4288e58b91.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203074&t=0b47d29f43a86fc5a2adae8bb5b60748","动漫桌面2.jpg");
        TestThread2 xiancheng3 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fc099d5c83bdffb17eff062d5298eb136f558b8fd.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203131&t=490c5ea40c0745215a8140d8118f22ec","动漫桌面3.jpg");
​
        //三个线程,同时按照从上往下的顺序执行   同时线程也不是立即执行,看CPU安排调度
        xiancheng1.start();
        xiancheng2.start();
        xiancheng3.start();
    }
​
}
​
//下载器
class WedDownLoader{
​
    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下载异常");
        }
    }
}

第二种:实现Runable接口

  • 定义实现MyRunnable类实现Runnable接口

  • 实现Run()方法,编写线程执行体

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

  • 注意:推荐使用Runnable对象,因为Java单继承的局限性

package com.waves.dxcdemo;
​
public class TestThread3 implements Runnable{
    @Override
    public void run() {
        //线程方法体
        for (int i = 0; i < 200; i++) {
            System.out.println("2021-11-28 18:22 多线程代码"+i);
        }
    }
​
    public static void main(String[] args) {
        //main方式,主线程
        TestThread3 thread3 = new TestThread3();
        //创建线程对象,通过线程对象来开启我们的线程,也就是代理
//        Thread thread = new Thread(thread3);
//        thread.start();
//合并为一句代码
        new Thread(thread3).start();
​
        for (int i = 0; i < 200; i++) {
            System.out.println("2021-11-28 18:22 学习多线程代码"+ i);
        }
    }
}

 


 

继承Thread类和实现Runnable接口-小结:

  • 继承Thread类

    • 子类继承Thread类具备多线程能力

    • 启动线程:子类对象.start()

    • 不建议使用:避免OOP单继承局限性

  • 实现Runnable接口

    • 实现接口Runnable具有多线程能力

    • 启动线程:传入目标对象+Thread对象.start()

    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

    • 1.实现runnable接口 买票案例 (线程不安全)

  1.实现runnable接口 买票案例 (线程不安全)

package com.waves.javathread.threadweb;
/**
 * 多线程同时操作一个对象
 * 买火车票例子
 *
 * 发现问题:多个线程操作同一个资源的情况下,线程不安全 ,会造成数据紊乱
 *
 */
public class ThreadImplRunnable01 implements Runnable{

    private int titckNumbers = 20;

    @Override
    public void run() {
        while (true){
            if (titckNumbers<=0){
                break;
             }
            //模拟延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->买到了第"+titckNumbers--+"票");
        }
    }

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

            new Thread(threadImplRunnable01,"买票者1").start();
            new Thread(threadImplRunnable01,"买票者2").start();
            new Thread(threadImplRunnable01,"买票者3").start();
    }
}

  2.实现runnable接口 模拟龟兔赛跑

package com.waves.javathread.threadweb;

/**
 * 模拟龟兔赛跑
 */
public class ThreadImplRunnable02 implements Runnable{

    private static String winner;

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (Thread.currentThread().getName().equals("兔子") && i == 50){
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束了
            if (success(i)){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"不");
        }
    }

    private boolean success(int pages){

        //判断是否有获胜者
        if (winner != null){
            return true;
        }{
            if (pages>=100){
                winner = Thread.currentThread().getName();
                System.out.println("获胜者是--->"+winner);
                return true;
            }
        }
        return false;
    }

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

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

第三种:实现Callable接口(了解即可)

  1. 实现callable接口,需要返回值类型

  2. 重写call方法,需要抛出异常

  3. 创建目标对象

  4. 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);

  5. 提交执行: Future<Boolean> result1 = ser.submit(xiancheng1);

  6. 获取结果: boolean r1 = result1.get();

  7. 关闭服务: ser.shutdown();

    注意: 代码案例 利用callable改造下载图片案例

package com.waves.javathread.threadweb.CallableInterface;


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

/**
 * 2023-0204 17:07:23
 * 狂神多线程
 * 线程创建方式三:实现callable接口
 * callable的好处
 * 1.可以定义返回值
 * 2.可以抛出异常
 */
public class ImplCallableDemo implements Callable<Boolean> {

    private String url; //下载图片的地址
    private String name; //要下载(保存)的图片名字哦

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

    @Override
    public Boolean call() {
        WedDownLoader wedDownLoader = new WedDownLoader();
        wedDownLoader.downLoader(url,name);
        //图片保存在根目录
        System.out.println("下载图片路径为"+url+"\n"+"下载的图片名字为"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ImplCallableDemo xiancheng1 = new ImplCallableDemo("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fd.zdqx.com%2Fshirt_20190710%2F014.jpg&refer=http%3A%2F%2Fd.zdqx.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641202820&t=637cea2d77c04500bdbef46abce0e64e","动漫桌面1.jpg");
        ImplCallableDemo xiancheng2 = new ImplCallableDemo("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fba794af4e3c6a4cf93b25f19e148dc4288e58b91.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203074&t=0b47d29f43a86fc5a2adae8bb5b60748","动漫桌面2.jpg");
        ImplCallableDemo xiancheng3 = new ImplCallableDemo("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fc099d5c83bdffb17eff062d5298eb136f558b8fd.jpg&refer=http%3A%2F%2Fi0.hdslb.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641203131&t=490c5ea40c0745215a8140d8118f22ec","动漫桌面3.jpg");

//        1. 创建执行服务:
        ExecutorService ser = Executors.newFixedThreadPool(3);
//        2. 提交执行:
        Future<Boolean> result1 = ser.submit(xiancheng1);
        Future<Boolean> result2 = ser.submit(xiancheng2);
        Future<Boolean> result3 = ser.submit(xiancheng3);
//        3. 获取结果:
        boolean r1 = result1.get();
        boolean r2 = result1.get();
        boolean r3 = result1.get();
//        4. 关闭服务:
        ser.shutdown();
    }
}

//下载器
class WedDownLoader{

    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下载异常");
        }
    }
}

 

Lamdba表达式

静态代理

package com.waves.javathread.staticproxy;

/**
 * @author waves
 * @date 2023/2/4 17:19
 *
 * 狂神多线程  静态代理:
 * 真实对象和代理对象都要实现同一个接口
 * 代理对象要代理真实角色
 *
 * 好处:
 *  代理对象可以做很多真实对象做不了的事情
 *  真实对象专注做自己的事情
 */
public class StaticProxy {

    public static void main(String[] args) {

        //lamda表达式写法
        new Thread(()-> System.out.println("I LOVE YOU!!!")).start();

        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappMarry();
    }

}


interface Marry{

    void HappMarry();
}

//实际角色 你去结婚
class You implements Marry{

    @Override
    public void HappMarry() {
        System.out.println("你要结婚了,超开心!");
    }
}

//代理角色,帮助你
class WeddingCompany implements Marry{
    //代理谁  --->>> 真实目标角色
    private Marry target;
    public WeddingCompany(Marry target){
        this.target = target;
    }

    @Override
    public void HappMarry() {
        before();
        this.target.HappMarry();  //真实对象
        after();
    }

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

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


}

 

推导Lamdba(怎么来的)

package com.waves.javathread.lamdba;

/**
 * @author waves
 * @date 2023/2/5 16:51
 *  推导lamdba表达式
 */
public class LamdbaDemo1 {

    //3.静态内部类
    static class Like2 implements ILike{
        @Override
        public void LamdbaIt() {
            System.out.println("i Like2 lamdba");
        }
    }

    public static void main(String[] args) {
        ILike iLike = null;
        iLike = new Like();
        iLike.LamdbaIt();

        iLike = new Like2();
        iLike.LamdbaIt();

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

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

        //6.借助Lamdba表达式简化
        iLike = ()-> {
            System.out.println("i Like5 lamdba");
        };
        iLike.LamdbaIt();
    }
}


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

//2.实现类
class Like implements ILike{

    @Override
    public void LamdbaIt() {
        System.out.println("i like lamdba");
    }
}

Lamdba简写

package com.waves.javathread.lamdba;

/**
 * @author waves
 * @date 2023/2/5 17:03
 *
 * 下列使用lamdba方式效果是一样的,但是简写了
 *
 *  总结:
 *      lamdba表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹.
 *      前提是接口为函数式接口
 *      多个参数也可以去掉参数类型,要去掉就都去掉,多参数必须加括号
 */
public class LamdbaDemo2 {

    public static void main(String[] args) {

        ILove love = null;
        ILoveD loved = null;

        /***
         * 单参数
         */
        //1.lamdba表达式简化
        love = (String str)->{
            System.out.println("i love you ---> "+str);
        };
        //2.简化 参数类型
        love = (str) ->{
            System.out.println("i love you ---> "+str);
        };
        //3. 简化括号
        love = str ->{
            System.out.println("i love you ---> "+str);
        };

        //4. 简化去掉花括号
        love = str -> System.out.println("i love you ---> "+str);


        love.love("小明");


        /**
         * 多参数  多参数
         */
        //1.lamdba表达式简化
        loved = (String str,String str2)->{
            System.out.println(str + " love you ---> "+str2);
        };
        //2.简化 参数类型
        loved = (str,str2) ->{
            System.out.println(str + " love you ---> "+str2);
        };
        loved.loved("小明","小红");

    }

}

interface ILove{
    void love(String str);
}

interface ILoveD{
    void loved(String str,String str2);
}

线程休眠

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

  2. sleep存在InterruptedException

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

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

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

线程礼让

  1. 礼让线程,让当前正在执行的线程暂停,但不阻塞

  2. 将线程从运行状态转为有序状态

  3. 让CPU重新调度,礼让不一定成功!看CPU心情

package com.waves.javathread.state;

/**
 * @author waves
 * @date 2023/2/7 21:52
 *
 * 测试线程礼让
 *但是礼让并不一定成功,看cpu心情
 */
public class ThreadYieldDemo {

}

class MyYidld implements  Runnable{

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

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

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

 

Join

  1. join合并线程,待此线程执行完成过后,在执行其他线程,其他线程阻塞

  2. 可以想象成插队

package com.waves.javathread.state;

/**
 * @author waves
 * @date 2023/2/7 22:03
 */
public class ThreadJoinDemo implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程VIP来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动线程
        ThreadJoinDemo threadJoinDemo = new ThreadJoinDemo();
        Thread thread = new Thread(threadJoinDemo);
        thread.start();

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

 

线程池

  1. 背景:经常创建和销毁,使用量 特别大的资源,例如:并发情况下的线程,对性能影响很大.

  2. 思路:提前创建多个线程,放入线程池,使用时直接获取,使用完放回池中.可以避免频繁创建销毁,实现重复利用. 例如生活中的公共交通工具

  3. 好处 :

    • 提高响应速度(减少了创建新线程 的时间)

    • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

    • 便于线程管理

      • corePoolSize:核心池子大小

      • maxmumPoolSize:最大线程数

      • keepAliveTime:线程 没有任务时最多保持多长时间后会终止

      •  

 

posted @ 2023-04-12 20:29  旧歌  阅读(19)  评论(0编辑  收藏  举报