Java多线程笔记

news2025/5/25 3:39:26

文章目录

    • 线程简介(Process AND Thread)
      • 本章核心概念
    • 线程实现(重点)
      • 线程创建(Thread、Runnable、Callable)
        • 1.Thread class 继承Thread类(重点)
          • 总结:注意
          • 案例:网图下载
        • 2.Runnable接口 实现Runnable接口(最为重点)
          • 跟直接extend Thread的区别:
          • 小结:注意
          • 案例:龟兔赛跑-Race
        • 3.Callable接口 实现Callable接口(学习阶段了解)
          • 案例:修改上面的图片下载
          • 实现静态代理对比Thread
          • 补充:Lamda表达式
          • Lamda小结:注意
    • 线程状态(5个)
    • 线程同步(重点)
          • 死锁产生的条件【重点】
          • Lock锁
          • 三大不安全例子:
          • 补充:JUC包下的CopyOnWriteArrayList
    • 线程通信问题
    • 高级主题
    • 总结:
      • 1. 线程创建(3种方式)
      • 线程同步

线程简介(Process AND Thread)

1.任务,进程,线程,多线程

  • 1.1 任务
    • 1.1.1 多任务:
      多任务处理是指用户可以在同一时间内运行多个应用程序,每个应用程序被称作一个任务。
      现实生活中很多同时要做很多件时间的例子,例如:边吃饭边玩手机,边蹲厕所边玩手机,边听歌边跑步等等。看起来是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件事。
  • 1.2 多线程
    • 1.2.1 多线程
      在程序中同时运行多个线程,每个线程都是独立运行的,即有自己的执行路径、栈、寄存器等资源,并且可以同步地访问共享数据
      原本汽车在一条道路很窄,道路拥挤效率极低,但是为了提高效率,会添加车道。
  • 1.3 进程
    • 1.3.1 程序概念
      程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
    • 1.3.2 进程概念
      进程是执行程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。
  • 1.4 线程
    • 1.4.1 线程概念
      通常,一个进程可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位。

本章核心概念

    1. 线程就是独立的执行路径
    1. 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
    1. main()称之为主线程,为系统的入口,用于执行整个程序。
    1. 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
    1. 对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制;
    1. 线程会带来额外的开销,如cpu调度时间,并发控制开销。
    1. 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

线程实现(重点)

线程创建(Thread、Runnable、Callable)

在这里插入图片描述

1.Thread class 继承Thread类(重点)
    1. 自定义线程类继承Thread类
    1. 重写run()方法,编写线程执行体
    1. 创建线程对象,调用start()方法启动线程
    1. 例:在这里插入图片描述
总结:注意

线程开启不一定立即执行,由CPU调度执行。执行的时候需要调用start()方法,不能直接调用run()方法。

案例:网图下载

使用多线程下载多个图片。

    1. 导入commons-io 2.6.jar包 https://commons.apache.org/proper/commons-io/
    1. 项目中新建lib目录,并右键jar包 -> Add as Library在这里插入图片描述
    1. 代码示例:
//  实现多线程下载图片
public class TestThread extends Thread{

    private String url; // 网络图片地址
    private String name; // 保存的文件名

    public TestThread(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) {
        TestThread thread = new TestThread("https://img0.baidu.com/it/u=3663508195,2907650417&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1697043600&t=50896d6d3a067f36990a6a406401f13a","2.jpg");
        TestThread thread2 = new TestThread("https://img0.baidu.com/it/u=3663508195,2907650417&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1697043600&t=50896d6d3a067f36990a6a406401f13a","3.jpg");
        TestThread thread3 = new TestThread("https://img0.baidu.com/it/u=3663508195,2907650417&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1697043600&t=50896d6d3a067f36990a6a406401f13a","4.jpg");

        thread.start();
        thread2.start();
        thread3.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方法出现问题");
        }
    }
}


    1. 效果展示:
      在这里插入图片描述
2.Runnable接口 实现Runnable接口(最为重点)
    1. 定义MyRunnable类实现Runnable接口
    1. 实现run()方法,编写线程执行体
    1. 创建线程对象,调用start()方法启动线程(执行线程需要丢入Runnable接口实现类,再调用start方法
    1. 如图:在这里插入图片描述
跟直接extend Thread的区别:

实现接口的时候再调用方面的区别,Runnable需要多一步把线程对象放到Thread里面

小结:注意
    1. 继承Thread类
    • 1.1 子类继承Thread类具备多线程能力
    • 1.2 启动线程:子类对象.start()
    • 1.3 不建议使用:避免OOP(面向对象程序设计(Object Oriented Programming))单继承的局限性。
    1. 实现Runnable接口
    • 2.1 实现接口Runnable具有多线程能力
    • 2.2 启动线程:传入目标对象+Thread对象.start()
    • 2.3 推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用在这里插入图片描述
    1. 买车票的例子(多个线程操作同一个资源的情况下,线程不安全,数据紊乱。)在这里插入图片描述
  • 现象(数据紊乱)在这里插入图片描述
案例:龟兔赛跑-Race

在这里插入图片描述

public class Race implements Runnable{
    // 胜利者
    private static String winner;
    @Override
    public void run() {
        // 来个100米的距离
        for (int i = 0; i <= 100; i++) {

            // 模拟兔子睡觉
            if (Thread.currentThread().getName().equals("兔子") && i%10==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 判断比赛是否结束
            boolean flag = gameOver(i);
            // 如果比赛结束,则停止程序
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
        }
    }

    // 判断是否完成比赛
    public boolean gameOver(int steps){
        // 判断是否为胜利者
        if (winner != null){ // 已经存在胜利者
            return true;
        }else {
            if (steps >= 100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is" + winner);
                return true;
            }
        }
        return false;
    }

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

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

  • 效果:在这里插入图片描述
3.Callable接口 实现Callable接口(学习阶段了解)
  • 1.在这里插入图片描述
案例:修改上面的图片下载
package com.dapeng.callcable;

import com.dapeng.Demo.TestThread;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

/**
 * @Description 通过Callable接口创建线程
 * 好处:
 *  1. 可以自定义返回值
 *  2. 可以抛出异常
 * @Author zhaopeng
 * @Date 2023/10/10 16:51
 */
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://img0.baidu.com/it/u=3663508195,2907650417&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1697043600&t=50896d6d3a067f36990a6a406401f13a","2.jpg");
        testCallable t2 = new testCallable("https://img0.baidu.com/it/u=3663508195,2907650417&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1697043600&t=50896d6d3a067f36990a6a406401f13a","3.jpg");
        testCallable t3 = new testCallable("https://img0.baidu.com/it/u=3663508195,2907650417&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1697043600&t=50896d6d3a067f36990a6a406401f13a","4.jpg");

        // 创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        // 提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);

        // 获取结果
        Boolean rs1 = r1.get();
        Boolean rs2 = r2.get();
        Boolean rs3 = r3.get();

        // 关闭服务
        ser.shutdownNow();
    }

}

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方法出现问题");
        }
    }
}


实现静态代理对比Thread
  • 总结:
    • 1.真实对象和代理对象都要事先同一个接口
      1. 代理对象要代理真实角色
  • 好处:
      1. 代理对象可以做很多真实对象做不了的事情
      1. 真实对象专注做自己的事情
package com.dapeng.staticProxy;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/10 17:09
 */
// 总结:静态代理模式总结:
//    真实对象和代理对象都要事先同一个接口
//    代理对象要代理真实角色
//    好处:代理对象可以做很多真实对象做不了的事情
//          真实对象专注做自己的事情
public class staticProxy {
    public static void main(String[] args) {
        You you = new You(); // 真实对象
		// 通过对比发现,括号里面的都是接口或者实现接口的类。
        new Thread(()-> System.out.println("斤斤计较")).start();
        new WeddingCompany(new You()).HappyMarry();
//        WeddingCompany weddingCompany = new WeddingCompany(you);
//        weddingCompany.HappyMarry();
    }
}

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 after() {
        System.out.println("结婚后,收拾房子");
    }

    private void before() {
        System.out.println("结婚前~~~~收钱");
    }
}

补充:Lamda表达式
    1. Lamda表达式概念:如下图
      在这里插入图片描述
    1. 为什么要使用Lamda表达式:在这里插入图片描述
    1. 了解函数式接口:在这里插入图片描述
    1. 推导Lamda表达式:在这里插入图片描述在这里插入图片描述在这里插入图片描述
    1. Lamda简化:在这里插入图片描述在这里插入图片描述
Lamda小结:注意
    1. lamda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹。
    1. 前提是接口为函数式接口。(只有一个方法的接口)
    1. 多个参数也可以去掉参数类型,要去掉都去掉,必须加上括号。

线程状态(5个)

    1. 线程状态:在这里插入图片描述在这里插入图片描述
    1. 线程的方法:在这里插入图片描述
    1. 停止线程:在这里插入图片描述
    • 3.1 小案例,让线程停止:
package com.dapeng.Demo;

/**
 * @Description 测试stop
 * 1. 建议线程正常停止----> 利用次数,不建议死循环
 * 2. 建议使用标志位----> 设置一个标志位
 * 3. 不要使用stop或者destory等过时或者JDK不建议使用的方法
 * @Author zhaopeng
 * @Date 2023/10/11 10:13
 */

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==100){
                // 调用stop的方法,切换标志位,让线程停止
                testStop.stop();
                System.out.println("线程停止了...");
            }
        }
    }
}

3.2 效果图:在这里插入图片描述

    1. 线程的休眠:(1000ms = 1s)在这里插入图片描述
    • 4.1 模拟网络延时和倒计时案例(写在一起了)
package com.dapeng.Demo;

/**
 * @Description
 * // 模拟网络延时: 放大问题的发生性
 * @Author zhaopeng
 * @Date 2023/10/11 10:34
 */
public class TestSleep implements Runnable{

    // 票数
    private int tickNums = 10;


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

    /**
     * 倒计时
     * @throws InterruptedException
     */
    public static void tenDown() throws InterruptedException {
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num<=0){
                break;
            }
        }
    }

    public static void main(String[] args) {
//        TestSleep ticket = new TestSleep();
//
//        new Thread(ticket,"小明").start();
//        new Thread(ticket,"老师").start();
//        new Thread(ticket,"黄牛").start();
        try {
            tenDown();
        } catch (InterruptedException e) {
            System.out.println("倒计时出错");
            e.printStackTrace();
        }
    }
}

    1. 线程的礼让:在这里插入图片描述
    • 5.1礼让测试Demo:
package com.dapeng.Demo;

/**
 * @Description
 * 测试礼让线程
 * 礼让不一定成功,看cpu心情
 * @Author zhaopeng
 * @Date 2023/10/11 10:50
 */
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() + "停止线程");
    }
}

    1. Join:在这里插入图片描述
    • 6.1 JoinDemo案例:
package com.dapeng.Demo;

/**
 * @Description
 * 测试Join方法
 * 想象为插队
 * @Author zhaopeng
 * @Date 2023/10/11 10:54
 */
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程VIP来了" + i);
        }
    }


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


        // 主线程
        for (int i = 0; i < 500; i++) {
            if (i == 200){
                thread.join();// 插队,main线程阻塞
            }
            System.out.println("main" + i);
        }
    }
}

效果图:
在这里插入图片描述
在这里插入图片描述

    1. 线程状态观测:在这里插入图片描述
    • 7.1 观察线程状态Demo:
package com.dapeng.Demo;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/11 11:06
 */

public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    System.out.println("要进入睡眠了");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("");
        });

        // 查看状态:
        Thread.State state = thread.getState();
        System.out.println(state); // NEW

        // 观察启动后
        thread.start();
        state = thread.getState();
        System.out.println("启动后:" + state);  // RUN

        //只要线程不终止,就一直输出状态
        while (state != Thread.State.TERMINATED){
            Thread.sleep(100); // 睡眠0.1秒
            state = thread.getState(); // 更新线程状态
            System.out.println( "睡眠的状态:" + state); // 输入状态
        }

    }

}


效果:
在这里插入图片描述在这里插入图片描述

    1. 线程的优先级:在这里插入图片描述
    • 8.1 优先级Demo案例:
package com.dapeng.Demo;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/11 11:32
 */
public class TestPriority {
    public static void main(String[] args) {
        // 主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());


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


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

        t2.setPriority(1);
        t2.start();

        t3.setPriority(Thread.MAX_PRIORITY);
        t3.start();

        t4.setPriority(4);
        t4.start();

        t5.setPriority(9);
        t5.start();
    }

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

效果:
在这里插入图片描述
性能倒置:优先级低的线程被cpu调度,但是优先级高的线程在等待(一般不会出现)

    1. 守护(daemon)线程:在这里插入图片描述
    • 9.1 守护线程案例Demo:
package com.dapeng.Demo;

/**
 * @Description
 * 守护线程测试
 * @Author zhaopeng
 * @Date 2023/10/11 11:48
 */
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you =new You();

        Thread godThread = new Thread(god,"god");
        godThread.setDaemon(true); // 默认是false表示用户线程,正常的线程都是用户线程。。。true表示守护线程

        godThread.start();

        new Thread(you,"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("你开心活着");
        }
        System.out.println("========== GoodBye 世界 ======");
    }
}

效果:在这里插入图片描述

线程同步(重点)

多个线程操作同一个资源

  • 1.并发:同一个对象被多个线程操作
    • 1.1 现实生活中,我们会遇到同一个资源,多人都想用的问题。比如食堂排队或者上厕所排队等,最原生的办法就是排队一个一个来。
    • 1.2 处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候我们就需要线程同步,线程同步就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
    1. 队列和锁:
      队列:简单来说就像去食堂排队,如果不排队就会乱了,所以需要依次排队。
      锁 :简单来说,一群人排队上厕所,排好队以后,占着厕所才能安心拉粑粑或者解手。
      形成同步的条件就是:队列加锁,可以解决线程的安全性。
    1. 线程同步:在这里插入图片描述
    1. 同步方法:在这里插入图片描述
    • 4.1同步方法的弊端:在这里插入图片描述
    • 4.2 同步块:在这里插入图片描述
    1. 死锁:在这里插入图片描述
    • 5.1 死锁Demo:
package com.dapeng.juctest;

/**
 * @Description 死锁:多个线程互相抱着对方需要的资源,然后形成僵持
 * @Author zhaopeng
 * @Date 2023/10/11 16:13
 */
public class DeadLock {
    public static void main(String[] args) {
        MakeUp makeUp = new MakeUp(0,"女孩");
        MakeUp makeUp2 = new MakeUp(2,"女孩222");
        
        makeUp.start();
        makeUp2.start();
    }
}

// 口红
class Lipstic {
}

// 镜子
class Mirror {
}

// 化妆
class MakeUp extends Thread {

    //需要的资源只有一份,用static来保证只有一份
    static Lipstic lipstic = new Lipstic();
    static Mirror mirror = new Mirror();

    int choice; // 选择
    String girlname; // 女孩

    MakeUp(int choice, String girlname) {
        this.choice = choice;
        this.girlname = girlname;
    }

    @Override
    public void run() {
        // 化妆
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeUp() throws InterruptedException {
        if (choice == 0){
            synchronized (lipstic){// 获得口红锁
                System.out.println(this.girlname + "获得口红锁");
                Thread.sleep(1000);
                synchronized (mirror){
                    System.out.println(this.girlname + "获得镜子锁");
                }
            }
            // 解决办法,将里面的同步块拿出来
            // synchronized (mirror){
            //       System.out.println(this.girlname + "获得镜子锁");
            //   }
        }else {
            synchronized (mirror){// 获得口红锁
                System.out.println(this.girlname + "获得镜子锁");
                Thread.sleep(2000);
                synchronized (lipstic){
                    System.out.println(this.girlname + "获得口红锁");
                }
            }
            // 解决办法,将里面的同步块拿出来
            // synchronized (lipstic){
            //        System.out.println(this.girlname + "获得口红锁");
            //    }
        }
    }
}

效果:程序卡死。

死锁产生的条件【重点】
  • 死锁产生的四个必要条件:
      1. 互斥条件:一个资源每次只能被一个进程使用。
      1. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
      1. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。
      1. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
        上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生
Lock锁
    1. Lock锁
      在这里插入图片描述
      可重入锁(ReentrantLock)
  • Demo:
package com.dapeng.juctest;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/11 16:40
 */
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2  = new TestLock2();

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

class TestLock2 implements Runnable {

    // 票
    int tickNums = 10;
    // 定义Lock锁
    private  final  ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try{
                // 加锁
                lock.lock();
                if (tickNums>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(tickNums--);
                }else {
                    break;
                }
            }finally {
                lock.unlock();
            }

        }
    }
}

    1. 格式:
  • 在这里插入图片描述
    1. synchronized 和 Lock 的对比:在这里插入图片描述
三大不安全例子:
    1. 买票不安全Demo:
package com.dapeng.syn;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/11 14:26
 */
public class UnsafeBuyTickets {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"张三").start();
        new Thread(buyTicket,"里斯").start();
        new Thread(buyTicket,"毛里求斯").start();
    }
}

class BuyTicket implements Runnable{

    // 票
    private int ticketNums = 10;

    boolean flag = true;// 外部停止方式

    @Override
    public void run() {
        // 买票
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

  // synchronized 同步方法,锁的是this(本身这个类)。 在需要解决并发问题的时候需要加上synchronized
  // private synchronized void buy() throws InterruptedException
    private  void buy() throws InterruptedException {
        // 判断是否有票
        if (ticketNums <= 0){
            flag = false;
            return;
        }
//        模拟延时
        Thread.sleep(100);
        // 买票
        System.out.println(Thread.currentThread().getName()+"拿到了第" + ticketNums--+"张票");
    }
}


效果:
在这里插入图片描述

    1. 银行取钱不安全Demo:
package com.dapeng.syn;

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/11 14:38
 */
public class UnsafeBank {
    public static void main(String[] args) {
        // 账户
        Account account = new Account(100, "结婚基金");

        Drawing you = new Drawing(account,50,"You");
        Drawing wife = new Drawing(account,100,"Wife");

        you.start();
        wife.start();
    }
}

// 账户
class Account{
    int money; // 余额
    String name; // 卡名

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

// 银行:模拟取款
class Drawing extends Thread{
    Account account; // 账户
    // 取了多少钱
    int drawingMoney;
    // 现在手里多少钱
    int nowMoney;
    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    // 取钱
    @Override
    public void run() {
    // 如果想解决并发问题,通过添加同步块synchronized(account){}来解决并发问题
        // 判断有没有钱
        if (account.money - drawingMoney < 0){
            System.out.println(this.getName() + "钱不够了,取不了");
            return;
        }
        // 线程睡眠,放大问题发生性
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 卡内余额 = 余额 - 你去的钱
        account.money = account.money - drawingMoney;
        // 你手里的钱
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name + "余额为:"+account.money);
        // Thread.currentThread().getName() = this.getName()
        System.out.println(this.getName() + "手里的钱" + nowMoney);
        super.run();
    }
}

效果:
在这里插入图片描述

    1. 不安全的List集合Demo:
package com.dapeng.syn;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description
 *
 * // 线程不安全的集合(因为多个线程操作list的时候,可能会把同一个位置给覆盖掉)
 * @Author zhaopeng
 * @Date 2023/10/11 15:11
 */
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
            // 通过添加同步代码块synchronized(list){}就会安全了
                list.add(Thread.currentThread().getName());
            }).start();
        }

        Thread.sleep(4000);
        System.out.println(list.size());
    }
}

效果:(长度不为10000,因为其中有的List集合某个下标被同时覆盖掉)
在这里插入图片描述

补充:JUC包下的CopyOnWriteArrayList
  • juc Demo:
package com.dapeng.juctest;

import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Description
 * 测试JUC里面的安全集合
 * @Author zhaopeng
 * @Date 2023/10/11 15:46
 */
public class TestJuc {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}

线程通信问题

(生产者消费者模式):生产者(Producer) -----> 数据缓存区 -------> 消费者(Consumer)

    1. 线程通信-分析:在这里插入图片描述
    1. Java提供了几个方法解决线程之间的通信问题:在这里插入图片描述
  • 3.1 解决方式1:管程法:在这里插入图片描述
  • 管程法Demo:
package com.dapeng.juctest;

/**
 * @Description
 * 测试:生产者,消费者-----》利用缓冲区解决:管程法
 * 生产者,消费者,缓冲区,产品
 * @Author zhaopeng
 * @Date 2023/10/11 17:03
 */
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 = 0; i < 100; i++) {
            System.out.println("生产了" + i + "只鸡");
            container.push(new Chicken(i));
        }
    }
}

// 消费者
class Consumer extends Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了第" + container.pop().i + "只鸡");

        }
    }
}

// 产品
class Chicken{
    int i ;
    public Chicken(int i){
        this.i = i;
    }
}

// 缓冲区
class SynContainer{

    // 需要一个容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0;

    // 生产者放入产品
    public synchronized void push(Chicken chicken){
        // 如果容器满了,就需要等待消费者消费
        if (count == chickens.length){
            // 通知消费者消费,生产等待
            try {
                System.out.println("生产者---------容器满了,赶紧消费");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 如果没有满,我们就需要丢入产品
        chickens[count] = chicken;
        count++;
        // 可以通知消费者消费了
        System.out.println("生产者---------赶紧消费");
        this.notifyAll();
    }

    // 消费者消费产品
    public synchronized Chicken pop(){
        // 判断能否消费
        if (count == 0) {
            // 等待生产者生产,消费者等待
            try {
                System.out.println("消费者---------容器没了,赶紧生产");
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 如果可以消费,消费
        count--;
        Chicken chicken =  chickens[count];

        // 吃完了通知生产者生产
        System.out.println("消费者---------我吃完了,赶紧生产");
        this.notifyAll();
        return chicken;
    }
}

    1. 2 解决方式2:信号灯法
      在这里插入图片描述
      通过一个标志位来判断,当为ture则可以唤醒,false则等待。
      信号灯法Demo:
package com.dapeng.juctest;

/**
* @Description
* @Author zhaopeng
* @Date 2023/10/11 17:33
*/
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){
               this.tv.play("喜羊羊与灰太狼");
           }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();
       }
   }
}

// TV
class Tv{
   // 演员表演,观众等待T
   // 观众观看,演员等待F
   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();
           }
       }
       // 通知观众表演
       this.notifyAll();
       System.out.println("观众观看了" + voice);
       this.flag = !this.flag;

   }
}

高级主题

    1. 使用线程池:在这里插入图片描述
      线程池Demo:
package com.dapeng.juctest;

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

/**
 * @Description
 * @Author zhaopeng
 * @Date 2023/10/11 17:54
 */
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());
        service.execute(new MyThread());

        //2. 关闭连接
        service.shutdown();
    }
}
class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +"--"+ i );
        }
    }
}

总结:

1. 线程创建(3种方式)

package com.dapeng.juctest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @Description 回顾线程的创建
 * @Author zhaopeng
 * @Date 2023/10/12 8:59
 */
public class TestNew {

    public static void main(String[] args) {
        new Thread1().start();
        new Thread(new Thread2()).start();

        FutureTask<String> futureTask = new FutureTask(new Thread3());
        new Thread(futureTask).start();

        try {
            String s = futureTask.get();
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}

//1. 继承Thread类
class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println("Thread1");
    }
}

// 2. 实现Runnable接口
class Thread2 implements Runnable {

    @Override
    public void run() {
        System.out.println("Thread2");
    }
}

// 3. 实现Callable解耦
class Thread3 implements Callable<String> {


    @Override
    public String call() throws Exception {
        System.out.println("Thread3");
        return "Thread3";
    }
}

线程同步

    1. (JUC安全类型的包)concurrent包下面的一些安全类
    1. 可以通过加synchronized到需要改变的变量的方法或者同步代码块中
    1. 可通过使用JDK5的Lock锁,显式定义锁:private final ReentrantLock lock = new ReentrantLock();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1091649.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

568A和568B两种线序

现状 现在大家都是采用568B的线序 线序 标准568A&#xff1a;橙白-1&#xff0c;橙-2&#xff0c;绿白-3&#xff0c;蓝-4&#xff0c;蓝白-5&#xff0c;绿-6&#xff0c;棕白-7&#xff0c;棕-8 标准568B&#xff1a;绿白-1&#xff0c;绿-2&#xff0c;橙白-3&#x…

GB28181学习(七)——设备视音频文件检索

要求 文件检索主要用于区域、设备、录像时间段、录像地点、录像报警为条件的查询&#xff1b;用Message消息发送检索请求和返回查询结果&#xff0c;传送结果的Message消息可以发送多条&#xff1b;文件检索请求和应答命令采用MANSCDP协议格式定义&#xff1b; 流程 目录检索…

电商爬虫API快速入门指南

​电子商务爬虫API​是一个公共数据爬虫API&#xff0c;旨在通过大多数电子商务网站收集大量实时本地化数据并搜索信息。这个数据收集工具作为一个值得信赖的解决方案&#xff0c;实现通过最复杂的电子商务网站收集公共信息。电子商务爬虫API适用于商业用例&#xff0c;诸如价格…

对Python3.8配置OpenCV4.5.5中

已下载好Pycharm3.8&#xff0c;但是Pycharm3.8中还未配置OpenCV&#xff0c;这里直接在命令提示符中输入 pip install opencv-python 后回车 来下载opencv_python进行配置。

[23] IPDreamer: Appearance-Controllable 3D Object Generation with Image Prompts

pdf Text-to-3D任务中&#xff0c;对3D模型外观的控制不强&#xff0c;本文提出IPDreamer来解决该问题。在NeRF Training阶段&#xff0c;IPDreamer根据文本用ControlNet生成参考图&#xff0c;并将参考图作为Zero 1-to-3的控制条件&#xff0c;用基于Zero 1-to-3的SDS损失生成…

台达DOP-B07S410触摸屏出现HMI no response无法上传的解决办法

台达DOP-B07S410触摸屏出现HMI no response无法上传的解决办法 台达触摸屏(B07S410)在上载程序时(显示No response from HMI)我以前的电脑是WIN7的,从来没出现过这样的问题,现在换成win10的,怎么都不行,(USB显示是一个大容量存储)换一台电脑(win10)有些行,有些不行…

二阶RC滤波器

二阶RC低通滤波器 二阶RC低通滤波器是一种常用的电路&#xff0c;用于滤除输入信号中高频部分&#xff0c;只保留低频部分。 一、原理 二阶RC低通滤波器由两个电阻&#xff08;R1, R2&#xff09;和两个电容&#xff08;C1, C2&#xff09;组成&#xff0c;他们的原理基于RC…

对地址解析协议ARP进一步探讨

之前在讨论MAC地址和IP地址时&#xff0c;顺便对ARP协议做了初步的总结 &#xff08;计网第三章&#xff08;数据链路层&#xff09;&#xff08;四&#xff09;&#xff08;MAC地址和IP地址、ARP协议、集线器和交换机&#xff09;&#xff09;&#xff0c;但是当时对ARP请求的…

Java线程安全问题

1、什么是线程安全问题 2、用程序模拟线程安全问题 代码说明&#xff1a; Account代表账户类DrawThread代表线程类ThreadTest运行线程类 Account类&#xff1a; package ThreadSave;public class Account {private double money; //余额private String cardId; //卡号publi…

基于天牛须优化的BP神经网络(分类应用) - 附代码

基于天牛须优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于天牛须优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.天牛须优化BP神经网络3.1 BP神经网络参数设置3.2 天牛须算法应用 4.测试结果&#x…

使用图像处理跟踪瞳孔(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

比较和同步数据库架构和数据:MssqlMerge Pro Crack

比较和同步数据库架构和数据 适用于Oracle、MySQL 和 MariaDB、SQL Server、PostgreSQL、SQLite、MS Access和跨 DBMS 场景 业界领先的文本比较工具中常用的两面板 UI 快速过滤器显示所有/新/更改/新更改 合并两个方向的更改 轻量级&#xff1a;跨 DBMS 工具小于 20 MB&#xf…

【Java学习之道】Swing框架与组件介绍

引言 在本篇文章中&#xff0c;我们将重点介绍Swing框架及其组件。Swing是一个用于构建图形用户界面的Java库&#xff0c;它提供了丰富的组件和布局管理器&#xff0c;可以帮助你轻松地创建出漂亮、功能强大的界面。无论你是刚开始学习Java还是已经有一些经验&#xff0c;我相…

VR太空舱体验馆VR神舟返回舱VR虚拟现实科技科普乐园

VR航天航空设备&#xff0c;寓教于乐 VR科技正成为航天航空领域的新宠。作为一种沉浸式的数字技术&#xff0c;VR(Virtual Reality&#xff0c;虚拟现实)能够为用户创造出逼真的虚拟环境&#xff0c;让人们仿佛身临其境。借助VR技术&#xff0c;我们可以带领学生和游客深入了解…

基于秃鹰优化的BP神经网络(分类应用) - 附代码

基于秃鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于秃鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.秃鹰优化BP神经网络3.1 BP神经网络参数设置3.2 秃鹰算法应用 4.测试结果&#xff1a;5.M…

python 对图片增加边框,logo贴图,获取图片exif参数,填写图片文本内容

完整代码 # 找到个可以下载免费字体的网站https://font.chi删除我naz.com/mi删除我anfei.html from PIL import Image, ImageDraw, ImageFont import exifreaddef photo_exif(image_path):f open(image_path, rb)tags exifread.process_file(f)# 打印所有照片信息&#xff0…

Redis Windows版下载,带安装包

1、直接下载解压缩至任意全英文路径 打开后会看到都有这个目录 2、如何启动redis&#xff1f; 双击redis-server.exe 即可启动redis服务 注&#xff1a;若想保持redis处于开启状态&#xff0c;不要关闭启动后的窗口 关闭窗口后&#xff0c;一般情况下redis服务会默认随之关闭…

【物联网+JAVA 】智慧工地源码

一、什么是智慧工地&#xff1f; 工地本身不拥有智慧&#xff0c;工地的运作是依赖于人的智慧。工地信息化技术&#xff0c;能够减少对人的依赖&#xff0c;使工地拥有智慧。 智慧工地&#xff0c;就是立足于“智慧城市”和“互联网”&#xff0c;采用云计算、大数据和物联网…

礼品小程序商城的作用是什么

礼品总是在不同场景中出现&#xff0c;拓展度高&#xff0c;线上线下经营商家众多&#xff0c;而在实际经营中&#xff0c;礼品企业经营痛点也不少。 互联网电商时代&#xff0c;人们更依赖于线上购物&#xff0c;商家可以通过线上经营卖货及赋能客户消费。 通过【雨科】平台搭…

openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文件参考

文章目录 openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文件参考99.1 参数说明99.2 认证方式 openGauss学习笔记-99 openGauss 数据库管理-管理数据库安全-客户端接入认证之配置文件参考 99.1 参数说明 表 1 参数说明 参数名称描述取值范…