JavaSE学习笔记总结day18(完结!!!)

news2024/12/23 14:11:47

今日内容

零、 复习昨日
一、作业
二、进程与线程
三、创建线程
四、线程的API
五、线程状态
六、线程同步

零、 复习昨日

晨考

一、作业

见答案

二、进程与线程[了解]

一个进程就是一个应用程序,进程包含线程
一个进程至少包含一个线程,大部分都是有多条线程在执行任务(多线程)

进程是系统资源分配的最小单位

线程是进程内部的一个执行任务

进程内多个线程是共享进程资源,线程是资源调度的最小单位

Java程序是否是多线程的吗?
答: 是! 至少有main线程,垃圾回收线程等等

三、创建线程[重点]

Thread

创建线程的方式有很多

  • 继承Thread
  • 实现Runnable接口
  • 使用Callable 和Future来完成
  • 使用线程池获得线程

3.1 继承Thread

步骤

  1. 自定义类
  2. 继承Thread
  3. 重写run方法
  4. 创建子类对象
  5. 调用start方法启动线程 [特别强调,开启新线程的方法是start]
    start方法内部执行时会调用run方法执行
public class MyThread extends Thread{

    // 重写方法,方法内部,就是该线程执行的任务
    @Override
    public void run() {
        for (int i = 1; i < 11; i++) {
            System.out.println("MyThread --> "+i );
        }
    }
}
    public static void main(String[] args) {

        // 创建一个线程
        MyThread myThread = new MyThread( );
        // 启动线程
        myThread.start();

        // ==============main线程执行====================
        for (int i = 1; i < 11; i++) {
            System.out.println("main-Thread" + i );
        }
        
        // 自定义线程和主线程同时执行,并且出现资源争抢情况
    }

使用匿名内部类的形式开启线程

        // ==========匿名内部类的方法开启线程==============
        new Thread(){
            @Override
            public void run() {
                for (int i = 1; i < 1001; i++) {
                    System.out.println("my-Thread-2 --> " + i );
                }
            }
        }.start();

myThread线程,main线程,my-Threah-2线程3个在争抢资源,如果没有出现效果,把主线程执行的代码放在最下方,因为主线程代码优先级较高,会优先执行…

3.2 实现Runnable接口

步骤

  1. 自定义类
  2. 实现Runnable接口
  3. 重写run方法
  4. 创建子实现类对象
  5. 把子实现类对象当构造方法的方法参数来创建Thread对象
  6. 由thread调用start方法开启线程
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {
            System.out.println("MyRunnable - 1 --> " + i);
        }
    }
}
    public static void main(String[] args) {
        // 4 创建实现类对象
        MyRunnable myRunnable = new MyRunnable( );

        // 5 把对象当构造方法参数,创建Thread对象
        Thread thread = new Thread(myRunnable);
        // 6 开启线程
        thread.start();
    }
        

匿名内部类的形式创建线程

// ============= 匿名内部类完成实现Runnable接口 ============
new Thread(new Runnable(){
    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {
            System.out.println("MyRunable-2 --> " + i );
        }
    }
} ).start();

3.3 区别

继承Thread开启线程和实现Runnable接口开启线程有什么区别?


  • 一个继承,一个实现
  • 继承Thread后,直接创建对象即可调用start开启线程
  • 实现Runnable接口的子类,还需要再创建Thread类对象才可以调用strat开启线程

从使用便捷度来说,继承Thread开启线程会方便一点…

继承Thread类就限制了该类再继承别的类,因为类只能单继承,而实现接口的同时可以继承别的类,并且还允许多继承.

所以推荐使用接口来实现.

四、线程的API[熟悉]

  • void start() 开启线程,会自动run方法执行线程任务
  • void run() 执行线程的方法
    • run() 方法是start开启线程后,JVM自动调用
  • void setName(String name) 给线程设置名字
    • 也可以通过构造方法,在创建线程时指定线程名
  • String getName() 获得线程的名字
  • static Thread currentThread() 返回当前正在执行的线程对象
  • setPriority(int priority) 设置优先级
    • 级别是1-10 ,默认是5
  • getPriority() 获得优先级
  • join() 加入线程,等待该线程终止
  • join(int milles) 加入线程,最大等待直到毫秒数
  • void setDaemon(boolean on) 设置守护线程
  • static void yield() 礼让线程,暂停当前线程,让其他线程执行
  • static void sleep(long milles) 线程休眠
    • 会让当前线程暂停执行,让出系统资源,让其他线程执行
    • 时间到,当前会继续和其他争抢资源执行
  • void stop() 结束当前线程,线程死亡(已过式)
    • 被废弃的原因是因为这个中断线程太直接太暴力…
package com.qf.thread;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 演示线程API
 */
public class TestThread3 {

    public static void main(String[] args) {
        testStop();
    }

    /**
     * 测试线程停止
     */
    public static void testStop() {
        Thread thread = new Thread("好人") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    if (i == 5000) {
                        Thread.currentThread().stop();
                    }
                    System.out.println(Thread.currentThread( ).getName( ) + " ---> " + i);
                }
            }
        };


        thread.start( );
    }


    public static void testSleep2() {
        Thread thread2 = new Thread("人") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    System.out.println(Thread.currentThread( ).getName( ) + " --->" + i);
                }
            }
        };

        Thread thread = new Thread("好人") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    if (i == 500) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace( );
                        }
                    }
                    System.out.println(Thread.currentThread( ).getName( ) + " ---> " + i);
                }
            }
        };


        thread.start( );
        thread2.start( );
    }


    /**
     * 演示线程休眠
     */
    public static void testSleep() {
        System.out.println("倒计时.." );
        for (int i = 5; i > 0; i--) {
            System.out.println(i );
            // 线程休眠1秒钟
            try {
                // 让当前主线程睡觉
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace( );
            }
        }
        System.out.println("发射!" );
    }


    /**
     * 演示加入线程
     */
    public static void testJoin() {
        Thread thread2 = new Thread("人") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    System.out.println(Thread.currentThread( ).getName( ) + " --->" + i);
                }
            }
        };

        Thread thread = new Thread("好人") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    if (i == 500) {
                        try {
                            // 线程1(好人)执行到500,让线程2(人)加入执行,直到该线程结束,当前线程再继续执行
                            //thread2.join();

                            // 线程1(好人)执行到500,让线程2(人)加入执行,让其运行指定毫秒时间
                            // 时间到,线程再争抢资源
                            thread2.join(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace( );
                        }
                    }
                    System.out.println(Thread.currentThread( ).getName( ) + " ---> " + i);
                }
            }
        };


        thread.start( );
        thread2.start( );
    }

    /**
     * 演示礼让线程
     */
    public static void testYield() {
        Thread thread = new Thread("好人") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    // 获得当前正在执行线程对象
                    if (i == 500) {
                        Thread.yield( );
                    }
                    System.out.println(Thread.currentThread( ).getName( ) + " ---> " + i);
                }
            }
        };

        Thread thread2 = new Thread("人") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    System.out.println(Thread.currentThread( ).getName( ) + " --->" + i);
                }
            }
        };

        thread.start( );
        thread2.start( );
    }


    /**
     * 演示守护线程
     */
    public static void testDaemon() {
        Thread thread = new Thread("皇上") {
            @Override
            public void run() {
                for (int i = 0; i < 1; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread( ).getName( ) + " ---> " + i);
                }
            }
        };

        Thread thread2 = new Thread("妃子") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    System.out.println(Thread.currentThread( ).getName( ) + " --->" + i);
                }
            }
        };

        // 开启前设置某个线程为守护线程
        thread2.setDaemon(true);
        // 一旦设置一个线程为守护线程,那么其他的线程就是被守护的


        thread.start( );
        thread2.start( );
    }


    /**
     * 演示关于线程优先级的方法
     */
    public static void testPriority() {
        Thread thread = new Thread("和谐号1") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread( ).getName( ) + " ---> " + i);
                }
            }
        };

        Thread thread2 = new Thread("复兴号2") {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    System.out.println(Thread.currentThread( ).getName( ) + " --->" + i);
                }
            }
        };

        // 默认优先级是5,
        System.out.println(thread.getName( ) + "--->" + thread.getPriority( ));
        System.out.println(thread2.getName( ) + "--->" + thread2.getPriority( ));

        // 设置优先级,最高是10,最低是1,不能越界
        // 注意不是一定优先级高的先执行,只是大概率上会是
        thread.setPriority(1);
        thread2.setPriority(10);

        thread.start( );
        thread2.start( );
    }

    /**
     * 演示关于线程名字的方法
     */
    public static void testName() {
        // 通过构造方法设置线程名
        Thread thread = new Thread("和谐号") {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    // 获得当前正在执行线程对象
                    System.out.println(Thread.currentThread( ).getName( ) + " ---> " + i);
                }
            }
        };

        Thread thread2 = new Thread( ) {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread( ).getName( ) + " --->" + i);
                }
            }
        };
        // 获得线程名
        // 默认线程名,从Thread-0开始
        //System.out.println(thread.getName( ));

        // 通过setName设置线程名
        thread2.setName("绿皮");

        //System.out.println(thread2.getName( ));

        thread.start( );
        thread2.start( );

        // 获得主线程的名字
        System.out.println(Thread.currentThread( ).getName( ));
    }
}

五、线程状态[面试]

线程的几种状态:

  • 创建/新建/初始
    • new 完线程对象
  • 就绪
    • 调用start
  • 等待/阻塞
    • join()或者sleep()
  • 运行
    • 执行run()方法
  • 死亡/销毁/终止
    • run方法执行完,或者调用stop()或者interrupt()中断等

清楚状态之间的转换

image-20230302170320045

六、线程安全[重点]

6.1 线程安全

  • 临界资源:共享资源(同⼀个对象),一次只可以有一个线程操作,才可以保证准确性

  • 原子操作:不可拆分的步骤,被视作一个整体。其步骤不能打乱

线程不安全: 1) 完整的步骤可能会被破坏 2) 线程内的数据可能被别的线程修改

6.2 线程安全方式

  • 同步方法
    • 给方法加锁,即设置同步锁关键词synchronized
    • 锁对象是当前对象,即this
  • 同步代码块
    • 将需要安全的代码使用同步代码块包裹,设置锁对象.
    • 锁可以是任意对象
    • 但是线程之前应该是同一把锁才能锁住,保证安全

其实就是给需要"同步",需要"安全",需要"步骤一致,不能打乱"的代码加锁.

需求: 要求打印机类的两个方法分别被两个线程同时执行.但是方法执行时不能被破坏,即方法执行时不能被另外一个线程抢走资源,要保证方法执行时步骤完整性.

package com.qf.sync;

import java.util.Date;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 打印机
 */
public class Printer {

    // 锁需要是同一对象
    private Object obj = new Object();

    // 打印机1号
    public synchronized void print1() {
       //synchronized (obj){
            System.out.print(1+" ");
            System.out.print(2+" ");
            System.out.print(3+" ");
            System.out.print(4+" ");
            System.out.print("\r\n");
        //}
    }


    // 打印机2号
    public synchronized void print2() {
        //synchronized (obj) {
            System.out.print("一 ");
            System.out.print("二 ");
            System.out.print("三 ");
            System.out.print("四 ");
            System.out.print("\r\n");
        //}
    }
}
    public static void main(String[] args) {
        Printer printer = new Printer( );

        // 线程1
        new Thread(){
            @Override
            public void run() {
                while(true){
                    printer.print1();
                }
            }
        }.start();

        // 线程2
        new Thread(){
            @Override
            public void run() {
                while(true){
                    printer.print2();
                }
            }
        }.start();
    }
}

换种方式写

public class TestSync2 {
	public static void main(String[] args) {
        // 线程1
        new Thread( ) {
            @Override
            public void run() {
                for (; ; ) {
                    synchronized (TestSync2.class) {
                        System.out.print(1 + " ");
                        System.out.print(2 + " ");
                        System.out.print(3 + " ");
                        System.out.print(4 + " ");
                        System.out.print("\r\n");
                    }
                }
            }
        }.start( );

        // 线程2
        new Thread( ) {
            @Override
            public void run() {
                for (; ; ) {
                    synchronized (TestSync2.class) {
                        System.out.print("一 ");
                        System.out.print("二 ");
                        System.out.print("三 ");
                        System.out.print("四 ");
                        System.out.print("\r\n");
                    }
                }
            }
        }.start( );
    }
}

6.3 转账案例

需求: 假设银行账户类,属性有卡号,余额,名字 ; 另外有ATM机器,机器内能接收账户,给ATM指定取的钱数.ATM支持多线程操作.

// 账户
public class Account {

    private String id;
    private double balance;

    public Account() {
    }

    public Account(String id, double balance) {
        this.id = id;
        this.balance = balance;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
public class ATM extends Thread {

    private Account account;
    private double money;
    // 取钱是多线程操作
    @Override
    public void run() {
        synchronized (ATM.class){
            if (account.getBalance() >= money) {
                // 模拟出钱的时间,目的是让出资源,让另外的线程执行,模拟争抢
                // 加锁后,sleep"抱着锁睡觉",是说线程休眠不会让出锁,即不会让出资源,其他线程无法执行,
                // 等着这个线程休眠结束并且执行完毕
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace( );
                }
                account.setBalance(account.getBalance() - money);
                System.out.println(Thread.currentThread().getName() + "取钱成功,取出" +money );
                System.out.println("余额:" + account.getBalance() );

            } else {
                System.out.println("你有多少钱,心里没数?!" );
            }
        }
    }

    public ATM() {
    }

    public ATM(Account account, double money,String name) {
        super(name);
        this.account = account;
        this.money = money;
    }

    public Account getAccount() {
        return account;
    }

    public void setAccount(Account account) {
        this.account = account;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}
public class Bank {

    public static void main(String[] args) {
        Account account = new Account("888888", 1000);
        new ATM(account,600,"小红").start();
        new ATM(account,600,"小黑").start();
    }
}

变式: 转账案例使用Runable接口来实现

练习

售票: 有一个窗口类售票,总共100张票,窗口不止一个可以多个窗口同时售票. 要保证票不能卖超,不能卖重复的票

6.4 线程安全的方式

不只有synchronized同步方法可以保证线程安全,其实还有很多

ThreadLocal

Volatile

Lock

ReentrantLock

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

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

相关文章

Win系统蓝牙设备频繁卡顿/断连 - 解决方案

Win系统蓝牙设备频繁卡顿/断连 - 解决方案前言常见网卡Intel无线网卡&#xff08;推荐&#xff09;Realtek无线网卡总结查看本机网卡解决方案更新驱动更换网卡&#xff08;推荐&#xff09;前言 无线网卡有2个模块&#xff0c;一个是WiFi&#xff0c;一个是蓝牙&#xff0c;因…

Kubernetes之存储管理(下)

动态卷供应 上篇文章讲述的持久性存储&#xff0c;是先创建pv&#xff0c;然后才能创建pvc。如果不同的命名空间里同时要创建不同的pvc&#xff0c;那么就需要提前创建好pv&#xff0c;这样才能为pvc提供存储。但是这种方式太过繁琐&#xff0c;可以使用storageClass&#xff…

yolov5算法,训练模型,模型检测

嘟嘟嘟嘟&#xff01;工作需要&#xff0c;所以学习了下yolov5算法。是干什么的呢&#xff1f; 通俗来说&#xff0c;可以将它看做是一个小孩儿&#xff0c;通过成年人&#xff08;开发人员&#xff09;提供的大量图片的学习&#xff0c;让自己知道我看到的哪些场景需要提醒给成…

MySQL底层存储B-Tree和B+Tree原理分析

1.B-Tree的原理分析 &#xff08;1&#xff09;什么是B-Tree B-树&#xff0c;全称是 Balanced Tree&#xff0c;是一种多路平衡查找树。 一个节点包括多个key (数量看业务)&#xff0c;具有M阶的B树&#xff0c;每个节点最多有M-1个Key。 节点的key元素个数就是指这个节点能…

Andorid:关于Binder几个面试问题

1.简单介绍下binderbinder是一种进程间通讯的机制进程间通讯需要了解用户空间和内核空间每个进程拥有自己的独立虚拟机&#xff0c;系统为他们分配的地址空间都是互相隔离的。如两个进程需要进行通讯&#xff0c;则需要使用到内核空间做载体&#xff0c;内核空间是所有进程共享…

FL2440(S3C2440A 芯片) 开发板开发笔记

FL2440(S3C2440A 芯片) 开发板开发笔记 开发板的拨码开关指南&#xff1a; FL2440 改 vnfg 飞凌嵌入式 www. witech. com. cn 09. 8. 22 1 开发板使用手册 version4. 0 FL2440 保定飞凌嵌入式技术有限公司 网站&#xff1a;http: //www. witech. com. cn http: //www. he…

动态规划之买卖股票问题

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓动态规划之买卖股票问题 &#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 动态规划算法本质上就是穷举…

synchronized底层

Monitor概念一、Java对象头二、Monitor2.1、Monitor—工作原理2.2、Monitor工作原理—字节码角度2.2、synchronized进阶原理&#xff08;优化&#xff09;2.3、synchronized优化原理——轻量级锁2.4、synchronized优化原理——锁膨胀2.5、synchronized优化原理——自旋优化2.6、…

VUE3-Cesium(加载GeoJSON数据)

目录 一、准备工作 1、新建vue项目 解决报错&#xff1a;使用nvm后找不到vue -V找不到版本 2、安装Cesium插件 3、安装 Element Plus、unplugin-vue-components 和 unplugin-auto-import 4、按需自动导入element-plus 测试按需自动导入element-plus是否配置成功 二、项…

2023年软考中级电子商务设计师考什么?

首先&#xff0c;电子商务设计师属于软考中级&#xff0c;因此难度也不是特别大。但并不是说就完全没有难度&#xff0c;难度还是有的&#xff0c;像上午题一般把基本知识点掌握了&#xff0c;是没什么问题的&#xff0c;重点就在于下午题会比较难。 接下来我们来剖析一下考试…

408考研计算机之计算机组成与设计——知识点及其做题经验篇目1:RAM与ROM

目录 一、RAM 1、特点 2、分类 ①SRAM ②DRAM 二、ROM 1、特点 2、分类 可能是小编的学习风格和大家的不一样&#xff0c;小编喜欢由难到易的学习风格&#xff0c;所以在408计算机考研的四门课中&#xff0c;先选择了最难的计算机组成与设计进行学习。近…

【C++】初识类和对象

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;C的学习之路 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言一、面向过程和面向对象初步认识二…

Spring核心模块—— BeanFactoryPostProcessorBeanPostProcessor(后处理器)

后置处理器前言Spring的后处理器BeanFactoryPostProcessor&#xff08;工厂后处理器&#xff09;执行节点作用基本信息经典场景子接口——BeanDefinitiRegistryPostProcessor基本介绍用途具体原理例子——注册BeanDefinition使用Spring的BeanFactoryPostProcessor扩展点完成自定…

Linux安装minio单机版

说明&#xff1a;因为前面记录了一下minio的使用&#xff0c;这里说一下minio的安装&#xff0c;只是单机版哦 环境准备&#xff1a;Linux系统 说明图&#xff1a; 1.创建文件夹命令 我的是安装在/usr/local 文件夹下面的创建文件夹命令 #进入目标文件夹 cd /usr/local#创建…

5 GateWay断言Predicate

每一个Predicate的使用&#xff0c;可以理解为&#xff1a;当满足条件后才会进行转发&#xff0c;如果十多个&#xff0c;那就是满足所有条件才会转发 断言种类 After&#xff1a;匹配在指定日期时间之后发生的请求。Before&#xff1a;匹配在指定日期之前发生的请求。Betwee…

常见摄像头接口USB、DVP、MIPI接口的对比

常见摄像头接口DVP、MIPI、USB的比较 引言 摄像头传感器已经广泛用于嵌入式设备了&#xff0c;现在的手机很多都支持多个摄像头。 在物联网领域&#xff0c;摄像头传感器也越来越被广泛使用。今天就来简单聊一聊几种常见的摄像头接口。 传感器与主控设备进行通信&#xff0…

基于S32K148快速调试TJA1101

文章目录1.前言2.TJA1101简介3.TJA1101调试3.1 硬件3.1.1 整体框图3.1.2 评估板3.1.2.1 参考原理图3.1.2.2 引脚说明3.1.3 转接板3.1.3.1 参考原理图3.1.3.2 模式配置3.1.3.3 原理介绍3.2 软件3.2.1 物理层&#xff08;TJA1101&#xff09;&#xff1a;3.2.2 数据链路层&#x…

05_Pulsar的主要组件介绍与命令使用、名称空间、Pulsar的topic相关操作、Pulsar Topic(主题)相关操作_高级操作、

1.5.Apache Pulsar的主要组件介绍与命令使用 1.5.1.多租户模式 1.5.1.1. 什么是多租户 1.5.1.2.Pulsar多租户的相关特征_安全性&#xff08;认证和授权&#xff09; 1.5.1.3.Pulsar多租户的相关特性_隔离性 1.5.1.4.Pulsar多租户的相关操作 1-获取租户列表 2-创建租户 3-获取配…

RocketMQ单机安装与启动

RocketMQ单机安装与启动系统要求下载地址安装步骤RocketMq启动NameServer查看是否启动成功启动BrokerProxy查看是否启动成功修改tool.sh测试消息产生消息的消费关闭服务器系统要求 下载地址 官网下载地址 二进制包是已经编译完成后可以直接运行的&#xff0c;源码包是需要编译…

Vant2 源码分析之 vant-sticky

前言 原打算借鉴 vant-sticky 源码&#xff0c;实现业务需求的某个功能&#xff0c;第一眼看以为看懂了&#xff0c;拿来用的时候&#xff0c;才发现一知半解。看第二遍时&#xff0c;对不起&#xff0c;是我肤浅了。这里侧重分析实现原理&#xff0c;其他部分不拓展开来&…