Java多线程

2023-12-22 13:57:58

以下内容是狂神Java课堂笔记

线程简介

在这里插入图片描述
一个进程中可以有多个线程

在这里插入图片描述

在这里插入图片描述

线程实现

线程创建

main线程
gc线程

三种创建方式

在这里插入图片描述

Thread类就实现了Runnable接口,所以Runnable接口最重要

Callable接口在工作后使用得比较多

Thread类

在这里插入图片描述
第一种创建方式:
交替执行

在这里插入图片描述

在这里插入图片描述

如果不是用start方法,直接run(),则会先执行run()方法体再执行主线程。
注意:线程开启不一定执行,由cpu进行调度。

**多线程下载图片的样例(略过)
安装commons-io的那个包:https://blog.csdn.net/rngweskt/article/details/84181530
狂神对应讲解的视频:https://www.bilibili.com/video/BV1V4411p7EF/?p=4&spm_id_from=pageDriver&vd_source=778e40652a97cf1a0f96297054b101d7

Runnable接口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
之所以要在代理Thread的括号中加入runnable类,是因为:
在这里插入图片描述

对比:

在这里插入图片描述

**多线程下载图片样例改用runnable接口:
https://www.bilibili.com/video/BV1V4411p7EF/?p=5&spm_id_from=pageDriver&vd_source=778e40652a97cf1a0f96297054b101d7

小结:
在这里插入图片描述
多线程并发问题

package thread;

// 多个线程同时操作同一个对象
// 买火车票为例
// 发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱
public class TestThread3 implements Runnable{

    // ticketNum
    private int ticketNums = 10;

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

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

        new Thread(ticket, "小米姑娘").start();
        new Thread(ticket, "老师").start();
        new Thread(ticket, "黄牛").start();
    }
}

在这里插入图片描述

Callable

*Callable:通过创建执行服务来实现多线程

在这里插入图片描述

静态代理

package thread;


// 静态代理模式总结:
// 1. 真实对象和代理对象实现同一个接口
// 2. 代理对象要代理真实角色

// 好处
// 1. 代理对象可以做真实对象做不了的事情
// 2. 真实对象专注于自己的事情
public class StaticProxy {
    public static void main(String[] args) {
//        You you = new You();
//        you.inductMarry();

        Company wedding = new Company(new You());
        wedding.inductMarry();

    }
}

interface Marry {
    void inductMarry();
}

// 真实角色
class You implements Marry {
    @Override
    public void inductMarry() {
        System.out.println("I will be married!");
    }
}

// 代理
class Company implements Marry {
    // 代理-》真实目标角色
    public Marry target;
    public Company(Marry target) {
        this.target = target;
    }
    @Override
    public void inductMarry() {
        System.out.println("decorating...");
        this.target.inductMarry(); // 真实对象
        System.out.println("end...");
    }
}

其中

Company wedding = new Company(new You());
wedding.inductMarry();

可以写成

new Company(new You()).inductMarry();

这就类似于(Runnable接口实现,用到了Lamda表达式)

new Thread(() -> System.out.println("I will be married!")).start();

Lamda表达式

在这里插入图片描述
在这里插入图片描述

函数式接口:

interface ILike {
    void lambda();
}

传统的一个接口声明和调用:

package lambda;
// 推导lambda表达式
public class TestLambda1 {
    public static void main(String[] args) {
        ILike like = new Like(); // 这里不用Like like = new Like();是为了不暴露Like类的实现
        like.lambda();
    }

}

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

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

使用静态内部类进行简化:

package lambda;
// 推导lambda表达式
public class TestLambda1 {
	// 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 like = new Like();是为了不暴露Like类的实现
        like.lambda();

        like = new Like2();
        like.lambda();
    }

}

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

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

转换成局部内部类:

package lambda;
// 推导lambda表达式
public class TestLambda1 {
    // 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 like = new Like();是为了不暴露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();
    }
}

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

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

再转换成匿名内部类:

package lambda;
// 推导lambda表达式
public class TestLambda1 {
    // 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 like = new Like();是为了不暴露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();
    }
}

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

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

进一步简化,即lambda:

package lambda;
// 推导lambda表达式
public class TestLambda1 {
    // 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 like = new Like();是为了不暴露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.");
    }
}

如果有参数:

interface ILike {
    void lambda(int a, int b);
}

那么对应的lambda表达式变为:

ILike like = (int a, int b) -> {
	System.out.println("I like lambda.");
}

可以去掉类型进行简化:(要去掉都去掉)

ILike like = null;
like = (a, b) -> {
	System.out.println("I like lambda.");
}

可以去掉括号进一步简化:(仅有一个参数)

ILike like = null;
like = a -> {
	System.out.println("I like lambda.");
}

可以去掉花括号进一步简化:(仅有一行语句)

ILike like = null;
like = (a, b) -> System.out.println("I like lambda.");

在这里插入图片描述

线程状态

在这里插入图片描述

在这里插入图片描述

线程方法

在这里插入图片描述

线程停止

在这里插入图片描述

表示这个方法已经过时了不建议使用

在这里插入图片描述

模拟线程停止:

package thread;

// 测试线程停止
// 1. 建议线程正常停止 --- 利用次数,不建议死循环
// 2. 建议使用标志位 --- 设置标志位
// 3. 不建议使用stop或者destroy等过时或JDK不建议使用的方法

public class TestStop implements Runnable{
    // 1. set flag
    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run..." + i++);
        }
    }

    // 2. set a public method to change flag
    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) {
                testStop.stop();
                System.out.println("Thread stop!");
            }
        }
    }
}

执行到900轮,线程flag设置为false,但是有可能存在之前线程还没执行结束的情况

在这里插入图片描述

线程休眠

在这里插入图片描述

模拟网络延时:放大问题的发生性

模拟倒计时:

在这里插入图片描述

打印系统时间:

在这里插入图片描述

每个对象都有一个锁,sleep操作不会释放锁。

线程礼让 yield

在这里插入图片描述

示例:礼让成功(不一定成功)

在这里插入图片描述

线程合并 join

在这里插入图片描述

示例:

在这里插入图片描述

线程状态观测

在这里插入图片描述

Thread.State state = thread.getState();

线程死亡或结束后不能再被启动了

线程优先级

在这里插入图片描述

示例:

package thread;

public class TestPriority {
    public static void main(String[] args) {
        // main
        System.out.println(Thread.currentThread().getName() + " --- " +Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);

        // 先设置优先级再启动
        t1.start();

        t2.setPriority(Thread.MAX_PRIORITY); // 10
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(8);
        t4.start();
    }
}

class MyPriority implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " --- " +Thread.currentThread().getPriority());
    }
}

在这里插入图片描述

守护线程 daemon

在这里插入图片描述

示例:

package thread;

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        User user = new User();

        // set daemon
        Thread thread = new Thread(god);
        thread.setDaemon(true);
        thread.start();

        new Thread(user).start();
    }
}

// daemon
class God implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("God bless!");
        }
    }
}

// user thread
class User implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("Yolo!");
        }
        System.out.println("---END---");
    }
}

在这里插入图片描述

即使守护线程没有推出循环,但是用户线程结束,程序就结束了。

线程同步(重点)

多个线程操作同一个资源

并发:统一对象被多个线程同时操作。

在这里插入图片描述

使用队列和锁解决线程同步的问题。

在这里插入图片描述

同步方法

在这里插入图片描述

弊端:

在这里插入图片描述

示例1:只有一个对象

在这里插入图片描述

示例2:不只一个对象

在这里插入图片描述

同步块:Obj是需要修改的资源

在这里插入图片描述

示例3:

// 原来不安全的时候,list始终无法到达10000
在这里插入图片描述

// 锁上list,线程安全了
在这里插入图片描述

JUC

// 安全的类 CopyOnWriteArrayList
在这里插入图片描述

死锁

多个线程抱着对方需要的资源,形成僵持

在这里插入图片描述

在这里插入图片描述

Lock锁

在这里插入图片描述

在这里插入图片描述

示例:

// 不安全的情况

package syn;

public class TestLock {
    public static void main(String[] args) {
        Tickets tickets = new Tickets();

        new Thread(tickets).start();
        new Thread(tickets).start();
        new Thread(tickets).start();

    }
}

// buy tickets
class Tickets implements Runnable {
    private int ticketNum = 10;
    @Override
    public void run() {
        while (ticketNum > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(ticketNum--);
        }
    }
}

加上锁后:

package syn;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        Tickets tickets = new Tickets();

        new Thread(tickets).start();
        new Thread(tickets).start();
        new Thread(tickets).start();

    }
}

// buy tickets
class Tickets implements Runnable {
    private int ticketNum = 10;

    // 定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                lock.lock(); // 上锁
                if (ticketNum > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(ticketNum--);
                } else {
                    break;
                }
            } finally {
                lock.unlock(); // 解锁
            }
        }
    }
}

注意这里不是:

package syn;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        Tickets tickets = new Tickets();

        new Thread(tickets).start();
        new Thread(tickets).start();
        new Thread(tickets).start();

    }
}

// buy tickets
class Tickets implements Runnable {
    private int ticketNum = 10;

    // 定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (ticketNum > 0) {
            try {
                lock.lock(); // 上锁
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(ticketNum--);
            } finally {
                lock.unlock(); // 解锁
            }
        }
    }
}

当多个线程同时访问共享资源(在这里是Tickets对象)时,使用锁是一种常见的方式来确保线程安全。在你的例子中,使用了ReentrantLock来实现线程同步。

在第一个代码片段中,每个线程的执行顺序如下:

  1. 线程1进入run方法,执行lock.lock(),获取锁。
  2. 线程1检查ticketNum大于0,然后打印ticketNum--,然后释放锁lock.unlock()
  3. 线程2进入run方法,执行lock.lock(),获取锁。
  4. 线程2检查ticketNum大于0,然后打印ticketNum--,然后释放锁lock.unlock()
  5. 线程3进入run方法,执行lock.lock(),获取锁。
  6. 线程3检查ticketNum大于0,然后打印ticketNum--,然后释放锁lock.unlock()

因为每个线程在获取锁后都会连续执行直到释放锁,所以输出是按顺序递减的。

在第二个代码片段中,每个线程的执行顺序如下:

  1. 线程1进入run方法,执行lock.lock(),获取锁。
  2. 线程1打印ticketNum--,然后释放锁lock.unlock(),然后执行while条件检查。
  3. 线程2进入run方法,执行lock.lock(),获取锁。
  4. 线程2打印ticketNum--,然后释放锁lock.unlock(),然后执行while条件检查。
  5. 线程3进入run方法,执行lock.lock(),获取锁。
  6. 线程3打印ticketNum--,然后释放锁lock.unlock(),然后执行while条件检查。

因为while条件检查发生在锁内部,每个线程在释放锁后会重新检查while条件,所以输出是同时递减的。

在第二个代码片段中,while条件检查发生在锁内部,这可能导致检查时机的混乱。因为每个线程在释放锁之后会重新检查while条件,所以在释放锁和再次检查条件之间,其他线程可能已经修改了共享的ticketNum值。

在这里插入图片描述

线程通信问题

线程协作

生产者消费者模式

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. 管程法
    在这里插入图片描述

示例:

package thread;

public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Producer(container).start();
        new Consumer(container).start();
    }
}

// producer
class Producer extends Thread {
    SynContainer synContainer;

    public Producer(SynContainer synContainer) {
        this.synContainer = synContainer;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synContainer.push(new Chicken(i));
            System.out.println("生产了" + i + "只鸡");
        }
    }
}

// consumer
class Consumer extends Thread {
    SynContainer synContainer;

    public Consumer(SynContainer synContainer) {
        this.synContainer = synContainer;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了" + synContainer.pop().id + "只鸡");
        }
    }
}

// chicken
class Chicken {
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

// synContainer
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) {
                throw new RuntimeException(e);
            }
        }

        // 如果没满
        chickens[count++] = chicken;

        // 通知消费者消费
        this.notify();
    }

    // 消费者消费产品
    public synchronized Chicken pop() {
        // 判断是否能消费
        if (count == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        
        // 可以消费
        count--;
        Chicken chicken = chickens[count];
        
        // 通知生产者已消费
        this.notify();
        return chicken;
    }
}
  1. 信号灯法
    在这里插入图片描述
package thread;

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 % 2 == 0) tv.play("show!!!");
            else tv.play("ad!!!");
        }
    }
}

// 消费者 -- 观众
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) {
                throw new RuntimeException(e);
            }
        }

        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) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("观看了" + voice);

        // 通知表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

线程池

在这里插入图片描述

package thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestPool {
    public static void main(String[] args) {
        // 1. 创建服务,创建线程池
        // newFixedThreadPool(线程池大小)
        ExecutorService service = Executors.newFixedThreadPool(10);

        // 执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        // 2. 关闭服务
        service.shutdown();
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

在这里插入图片描述
很类似collable

文章来源:https://blog.csdn.net/qq_43606119/article/details/135031406
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。