JavaSE多线程(6)学习总结

JavaSE多线程 编写一个类,直接继承java. lang. Thread,重写run方法。 阅读详情

在我们的生活中,比如公司的笔试面试,又或者是开始了解计算机底层的知识,都需要我们了解进程、线程等相关的概念。在这里仅此个人做一个总结,本文参考多个博客内容和视频以完善多线程知识。

1、多线程简介

1.1 简单介绍各个名词

这里要区分程序、进程、线程、并行、并发、多任务等相关的概念。

  • 程序:程序是为了完成程序员所期待完成的业务,用某种语言编写的一组指令集合,即一段静态代码、对象。
  • 进程(process):是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。它在操作系统运行,是系统分配资源的单位。
  • 线程(Thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。它是CPU调度和执行的单位
  • 并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事。
  • 并发:一个CPU(采用时间片)同时执行多个任务,比如秒杀平台,多个人做同件事。但是在计算机看来时间差过短以致于直观上是一种同时进行的感觉。
  • 多任务:比如边吃饭边玩手机,但是多任务本质上是一个时间做一件事情

1.2 核心概念

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

2、线程的创建

线程的创建方式:

  • 继承Thread类
  • 实现Rrunnable接口
  • 实现Callable接口

2.1 Thread

  1. 自定义线程类继承Thread类
  2. 重写**run()**方法,编写线程执行体
  3. 创建线程对象,调用**start()**方法启动线程

线程开启时不一定立即执行,而且由CPU来安排调度。

缺点:

  • 在于会造成线程不安全情况,即资源紊乱。
  • 避免OOP单继承局限性。

2.2 Runnable接口

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程

实际上第一个Thread继承中,Thread实现了Runnable接口。在避免单继承局限性上会更加灵活多变,并且方便同一个对象被多个线程使用。

public class TestThread implements Runnable {
        //票数
        private int ticketNum = 10;

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

        public static void main(String[] args) {
                TestThread ticket = new TestThread();
                new Thread(ticket, "小明").start();
                new Thread(ticket, "老师").start();
                new Thread(ticket, "黄牛党").start();
        }
}

在这里插入图片描述

这里是实现Runnable接口的方式。

2.3 实现Callable接口(了解)

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行:Future result1 = ser.submit(t1);
  6. 获取结果:boolean r1 = result1.get();
  7. 关闭服务:ser.shutdownNow();

3、线程状态

img

线程状态包括新建、就绪、运行、阻塞和死亡阶段。

  • 新建状态:

    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  • 就绪状态:

    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  • 运行状态:

    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  • 阻塞状态:

    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态:

    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

停止线程

不推荐使用JDK提供的stop()、destory()方法。【已废弃】

推荐线程自己停下来

建议使用一个标志位进行终止变量。当flag=false,则种植者线程运行。

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

意外:

  • 线程进入while循环体内
  • main线程执行stop方法切换标志位并输出
  • 线程执行while方法体的输出
  • 所以会产生线程多输出一次的现象。

在这里插入图片描述

线程休眠

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

  2. sleep存在异常InterruptedException;

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

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

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

    public class TestSleep {
        public static void main(String[] args) {
            try {
                tenDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        //模拟倒计时
        public static void tenDown() throws InterruptedException {
            int num = 10;
            while (true) {
                Thread.sleep(1000);
                System.out.println(num--);
                if (num <= 0) {
                    break;
                }
            }
        }
    }
    

在这里插入图片描述

线程礼让

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

  2. 将线程从运行状态转为就绪状态

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

    //测试礼让线程
    //礼让不一定成功,看CPU心情
    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()+"线程停止执行");
        }
    }
    

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    所以此时礼让不一定会成功。全然看CPU心情。

Join

  1. Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

  2. 可以想象成插队

    //测试Join方法--->想象成插队
    public class TestJoin 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{
            //启动我们的线程
            TestJoin testJoin = new TestJoin();
            Thread thread = new Thread();
            thread.start();
            //主线程
            for(int i=0;i<1000;i++){
                if(i==200){
                    testJoin.run();
                    thread.join();  //插队
                }
                System.out.println("main"+i);
            }
        }
    }
    

    在这里插入图片描述

观察测试线程的状态

public class TestState{
    public static void main(String[] args) throws InterruptedException{
        Thread thread = new Thread( ()-> {
        for (int i = 0; i < 5; i++) {
            try {
                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(1000);
            state = thread.getState();  //更新线程状态
            System.out.println(state);  //输出状态
        }
    }
}

在这里插入图片描述

【注意】:

在这里插入图片描述

线程不能启动两次,即死亡之后的线程就不能再启动了。这里是发生了运行时异常(即继承到RuntimeException异常)

执行完了之后就““死”了,只能在new一个执行。

线程优先级

  1. Java提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
  2. 线程的优先级用数字表示,范围从1~10.
    • Thread.MIN_PRIORITY = 1;
    • Thread.MAX_PRIORITY = 10;
    • Thread.NORM_PRIORITY = 5;
  3. 使用以下方式改变或获取优先级
    • get.Priority.setPriority(int xxx)
  4. 优先级高不一定会先执行,还要看CPU调度谁。只是优先级高被调度的几率大。
//测试线程的优先级
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);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

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

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

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

        t4.setPriority(MAX_PRIORITY);   //MAX_PRIORITY=10
        t4.start();

        t5.setPriority(-1);
        t5.start();

        t6.setPriority(11);
        t6.start();
    }
}

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

在这里插入图片描述

其中,优先级的设定建议在start()调度前。并且要注意参数不能是负数,也不能大于10,不然会报运行时异常。

守护(daemon)线程

  1. 线程分为用户线程和守护线程
  2. 虚拟机必须确保用户线程执行完毕,如main()
  3. 虚拟机不用等待守护线程执行完毕,如gc()
  4. 比如后台记录操作日志,监控内存,垃圾回收等…
//上帝守护你
public class TestDaemon{
    public static void main(String[] args){
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);
        thread.setDaemon(true); //默认是false表示是用户线程,正常的线程都是用户线程
        thread.start(); //上帝守护线程启动
        new Thread(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!World!======");
        }
    }

在这里插入图片描述

可以看到,直到你到100岁死了上帝就不守护你了,但是你死了上帝还要运行一会,因为虚拟机停止需要一点时间。

4、线程同步

线程同步

即等待机制。多个需要同时访问这个对象的线程进入这个对象的等待池形成队列。前面的线程使用完毕下一个线程再使用。

形成条件:队列+锁,即保证安全性

  • 锁机制synchronized(隐式锁)

存在问题:

  • 加锁会影响性能(上下文切换、调度延时)

  • 导致其他需要此锁的线程挂起

  • 优先级高的等待优先级低的线程释放锁会导致优先级倒置

同步块

synchronized(obj){}

其中obj推荐使用的是共享资源,锁的是变化的量即增删改

死锁

两个线程各自占有共享资源并且要互相等待其他线程占有的资源才能运行导致的两个或多个线程都在等待对方释放资源。

必要条件:

在这里插入图片描述

Lock锁(显式锁)

java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。

通过显式定义同步锁对象实现同步。常用的是ReentrantLock,可以显式加锁和释放锁。

在这里插入图片描述

lock锁和synchronized的区别:

在这里插入图片描述

5、线程协作

线程通信

应用场景:生产者和消费者问题
  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的从产品被消费者取走为止。
  • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。
分析:
  • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费。
  • 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费。
  • 在生产者消费者问题中,仅有synchronized是不够的
    • synchronized可组织并发更新同一个共享资源,实现了同步
    • synchronized不能用来实现不同线程之间的消息传递(通信)

img

解决生产者消费者问题

管程法
  1. 并发协作模型“生产者/消费者模式”—>管程法
    • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
    • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
    • 缓冲区:不能直接使用生产者的数据,他们之间有个“缓冲区”
  2. 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
//生产者消费者模型-->利用缓冲区解决:管程法
//需要的四个对象:生产者,消费者,产品,缓冲区
public class TestPC{
    public static void main(String[] args){
        SynContainer container = new SynContainer();

        new Productor(container).start();
        new Consumer(container).start();
    }
}

//生产者
class Productor extends Thread{
    SynContainer container;
    public Productor(SynContainer container){
        this.container = container;
    }

    //生产
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            container.push(new Chicken(i));
            System.out.println("生产了"+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().id+"只鸡");
        }
    }
}

//产品
class Chicken{
    int id; //产品编号
    public Chicken(int id){
        this.id = id;
    }
}

//缓冲区
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){
                e.printStackTrace();
            }
        }
        //如果没有满,我们就需要丢入产品
        chickens[count] = chicken;
        count++;

        //可以通知消费者消费了
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Chicken pop(){
        //判断能否消费
        if(count==0){
            //等待生产者生产,消费者等待
            try{
                this.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];

        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }
}

在这里插入图片描述

具体讲解:

前面消费者、生产者和产品都很好理解。我们可以当做管程法在消费者和生产者中间有一个中间商(比如卖菜和买菜都需要在市场中进行。)

到了缓冲区部分,我们需要实现市场的方法,即能够卖出也能够提供。

//生产者放入产品
    public synchronized void push(Chicken chicken){
        //如果容器满了,就需要等待消费者消费
        if(count == chickens.length){
            //通知消费者消费,生产者等待
            try{
                this.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        //如果没有满,我们就需要丢入产品
        chickens[count] = chicken;
        count++;
        //可以通知消费者消费了
        this.notifyAll();
    }
//消费者消费产品
    public synchronized Chicken pop(){
        //判断能否消费
        if(count==0){
            //等待生产者生产,消费者等待
            try{
                this.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];

        //吃完了,通知生产者生产
        this.notifyAll();
        return chicken;
    }

注意,在这两个方法中都应该在方法上加锁。正如消费者和生产者之间的合法交易需要市场监督一样,市场的两个方法都需要提供锁机制以保证线程有一定的安全。

信号灯法
//测试生产者消费者问题2:信号灯法,标志位解决
//演员不是即时表演,是拍出一部片并上传,然后通知观众来看
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();
        }
    }
}

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();
            }
        }
        System.out.println("观看了:"+voice);
        //通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

在这里插入图片描述

线程池

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

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

  3. 好处:

    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
    • 便于线程管理(…)
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大线程数
      • keepAliveTime:线程没有任务时最多保持多长时机后会终止
  4. JDK 5.0 q起提供了线程池相关API:ExecutorService和Executors

  5. ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

    • void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
    • Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
    • void shutdown():关闭连接池
  6. 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());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();
    }
}

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

ble command):执行任务/命令,没有返回值,一般用来执行Runnable

  • Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
  • void shutdown():关闭连接池
  1. 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());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();
    }
}

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

在这里插入图片描述

以上就是Java多线程结合视频、文档和博客的总结。感谢阅读

《JavaSE系列教程》 本专栏内容是为零基础正在学习Java的学习者量身定制的学习路线及方案,本教程字数超20w+,插图超过400+,知识体系极其全面,案例极其丰富。本配备有专门的学习群及学习视频,读者可加学习群与正在学习该阶段的人一同学习,有任何不懂的也可以观看于此教程配套的学习视频。 阅读详情

相关推荐

vscode使用continue插件连接LM studio的模型

本文详细介绍了如何使用VSCode的Continue插件连接LM Studio模型。主要步骤包括:在LM Studio中加载模型并设置相关参数,启动API服务,修改Continue配置文件以集成LM Studio模型,并进行测试以验证连接是否成功。通过这些步骤,用户可以将LM Studio中的预训练模型与VSCode的Continue插件无缝对接,实现高效的自然语言处理和编程辅助功能。

weixin_43590837的博客 8628

多线程

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

qq_40050233的博客 256

微信公众号数据采集实战:历史文章、互动数据与评论详情全解析

本文提供了一套完整的微信公众号数据采集实战方案,详细解析了如何通过接口调用获取公众号的历史文章列表、单篇文章的互动数据以及详细的用户评论。文章从环境准备、核心参数解析到分页逻辑、批量处理和异常处理,提供了可运行的Python代码示例,旨在帮助开发者高效、稳定地构建自动化采集工具,用于竞品分析、内容研究和舆情监控。

u5v6w7的博客 1596

java多线程小游戏_java控制台贪吃蛇小游戏(多线程版)

多线程版贪吃蛇 游戏说明: 本游戏运行在控制台中,直接运行SnakeTest.java文件,就能在控制台看到游戏画面,每按一个方向再按回车键就能控制蛇移动的方向a:向左移动d:向右移动s:向下移动w:向上移动其他字母键:暂停源代码:Snake.javaimport java.util.LinkedList;import java.util.Random;import java.util.Scann...

weixin_39590739的博客 720

Matlab TreeBagger实战:用随机森林预测房价的5个关键步骤(附完整代码)

本文详细介绍了使用Matlab的TreeBagger工具包构建随机森林模型预测房价的完整流程。通过波士顿房价数据集实战,涵盖数据预处理、模型构建、参数调优、特征重要性分析及高级应用(如分位数回归)等5个关键步骤,并附有完整代码,帮助读者快速掌握这一强大的回归分析工具。

ol789012的博客 444

DeepSeek在实时计算与流处理系统中的突破性实践

对于技术决策者而言,现在正是将DeepSeek实时计算技术纳入企业架构蓝图的关键时机,那些能够率先构建实时智能能力的企业,将在数据驱动的商业竞争中赢得决定性优势。DeepSeek通过创新的流处理架构,成功将复杂事件处理(CEP)的延迟降低到亚秒级,同时保证了计算结果的精确性。DeepSeek研发的神经架构搜索(NAS)与知识蒸馏联合优化框架,可在保持模型精度的前提下,将参数量压缩至原始模型的1/100,实现边缘设备的实时AI推理。随着5G和物联网技术的普及,对实时处理能力的需求将呈指数级增长。

Programming Talk 290

java多线程游戏设计-弹球游戏(包含全部代码)

java多线程游戏设计

4174

数据分析!Python爬虫爬取B站UP主数据并分析涨粉规律(附可视化图表)

爬取更多UP主:扩充列表,增加需要分析的UP主UID数量爬取更多作品:修改的参数,增加每个UP主的作品爬取页数新增分析维度:在类中添加方法,可分析发布时间(如定时发布对涨粉的影响)、内容标签(如游戏/知识区涨粉差异)优化可视化样式:调整方法中的图表类型、颜色、标题等,适配个人展示需求播放量与粉丝数强正相关:从相关性热力图可看出,总播放量与粉丝数的相关系数通常在0.8以上,说明曝光量是涨粉的核心基础作品数量存在“边际效应”

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1226

java多线程提交任务并返回结果

java多线程提交任务并返回结果 最近工作中有需要短时间内提交大量请求,并获取响应结果,最终选择了CompletionService接口来实现,它整合了Executor和BlockingQueue的功能。你可以将Callable任务提交给它去执行,然后使用类似于队列中的take方法获取线程的返回值。具体jdk的解释是:将生产新的异步任务与使用已完成任务的结果分离开来的服务。生产者 submit ...

寒溏渡的博客 5880

鲁棒优化 (Robust Optimization) 算法详解及案例分析

鲁棒优化是一种用于处理不确定性优化问题的方法。其目标是在不确定性参数的可能取值范围内,找到一个对不确定性具有鲁棒性的解。

闲人编程的博客 4328

SpringCloud Alibaba - Seata 实现高可用和异地容灾

这里不仅仅要实现高可用,还需要实现异地容灾. 异地容灾就是说,如果你整了好几个节点部署起来,全部放到一个机房里面,那么如果这个机房出了故障,就意味着整个集群就完了,所以对于一个大型的集群来讲,都会把这些服务异地部署.问题2:如果我们要实现异地容灾,也就说某一个机房挂了,需要快速的切换到另一个可用的机房,但是这个配置在配置文件中写死了,如果要改配置文件,还需要重启服务器,这是我们不希望看到的,怎么办?进入 seata/bin 目录,运行 seata-server.bat 文件,默认端口为 8091。

CYK_byte的博客 975

【学习笔记之JavaSE】-6.多线程

文章目录0.基本概念1.创建2.线程的同步 0.基本概念 线程与进程 1.进程(process):是程序(指令集合)的一次执行过成,动态的(的产生、存在和消亡)即生命周期。进程作为资源分配的单位,一个进程中的多个线程共享相同的内存单元/内存地址空间。 2.线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(p...

qq_40265247的博客 227

第四章:无感FOC的平滑启动艺术:告别“咔哒”声与抖动

无感FOC启动技术面临"先有位置还是先有旋转"的核心矛盾,导致常见故障包括位置失准、负载突变等。基础方案采用开环I/F控制,但存在重载启动困难等局限性。进阶方案结合IPD位置检测技术,通过脉冲电压注入法精准定位转子初始位置。关键技术在于开环到闭环的平滑切换,采用混合观测器设计和相位对齐技术。量产优化包括自适应参数整定和故障恢复机制,确保启动可靠性和一致性。该系统工程方案可有效解决启动抖动、异响等问题,提升用户体验。

汽车软件开发 733

JavaSE4-6学习总结(Map,异常,多线程(参考之前))

4.1 Map 1、两种遍历方式 通过获取所有的键,来遍历找值 Map &amp;amp;lt;K ,V&amp;amp;gt; map = new HashMap&amp;amp;lt;K,V&amp;amp;gt;(); //获取所有的键 Set&amp;amp;lt;K&amp;amp;gt; set = map.keySet(); //遍历set集合 Iterator&amp;amp;lt;K&amp;amp;gt; it

有文化的博客 262

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

JavaSE6Tutorial:项目的核心功能/场景

JavaSE6Tutorial 是一个开源项目,主要提供了Java SE 6版本的基础教程和详细指南。该项目旨在帮助初学者和开发者深入了解Java编程语言的核心概念、语法和应用。 ## 项目介绍 JavaSE6Tutorial 项目包含了一系列的Markdown格式文档,这些文档详细介绍了Java SE 6的各个方面,从基础语法到高级特性,例如泛型、反射、多线程等。项目的内容结构清晰,易于跟随...

gitblog_00171的博客 327

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

JavaSE学习经验整理

JavaSE学习经验整理JavaSE学习经验整理首先为了新手小白准备了些常用的快捷键知识点Java语言概述入门程序:HelloWorld案例书写规范(公司很在意书写规范,没有规则不成方圆)Java基础面向对象面向对象的思想特点类与对象Java程序的开发,设计和特征匿名对象封装privatethis关键字构造方法static关键字继承final关键字多态抽象类接口包导包权限修饰符常见的修饰符内部类具...

weixin_43949154的博客 2132

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

javaSE学习笔记_目录

IO字节流 javaSE (二十九)IO.read()输出是int的原因、fileoutputstream的追加功能、用byte[]优化拷贝文件、Buffered缓冲区内存示意图 javaSE (三十)IO流异常处理、图片加密处理、输入文件路径并拷贝文件内容、在文件中键入内容 计算机基础知识 javaSE (一)数据类型转换、++/–运算符 javaSE (二)逻辑/位运算符、左移右移运算符、sw...

PizAn的博客 7717

怎样正确的度过JavaSE阶段真的很重要!(详细知识内容+资料)

不要小看JavaSE的这些基础章节,打好基础很重要!巧了,我这里涵盖了所有Java基础!

奈何的人生 2万+

Javase学习复习D16[多线程]

主线程 : CPU和内存之间开辟的转门为main方法服务的线程指的是两个或者两个以上的线程在执行的过程中由于竞争同步锁而产生的一种阻塞现象;如果没有外力的作用,他们将无法继续执行下去,这种情况称之为死锁根据上图所示:线程1正在持有锁1,但是线程1必须再拿到锁2,才能继续执行而线程2正在持有锁2,但是线程2需要再拿到锁1,才能继续执行此时两个线程处于互相等待的状态,就是死锁,在程序中的死锁将出现在同步代码块的嵌套中当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。

ysy1648067239的博客 1049

JavaSE-多线程

这里写自定义目录标题学习主题:JavaSE-多线程学习前言:线程实现:线程的状态: 多线程 学习主题:JavaSE-多线程 多线程的本质就是多条路径同时执行 学习前言: 1.一条路径 程序按顺序执行,线程安全,但是CPU利用率低 2.两条路径 即为多线程,可以并行执行,但是存在线程不安全问题 3.进程 线程是操作系统的运行调度最小单位,是进程的运行单位 a:一个进程可以有多个线程 b:一个进程中的线程共享相同的内存单元 4.线程的优缺点 a:资源利用率高,程序响应快,程序设计存在便利 b:设计复杂(复

weixin_44158590的博客 464

重学JavaSe后的补充

文章目录1.三元运算符:2.基本数据类型转换:1)自动类型转换:2)强制类型转换:3.if-else和switch的区别:switch使用时的注意事项:4.三种循环的区别:5.break关键字的常见用法:6.continue关键字的用法: 1.三元运算符: 需要三个数据才可以进行操作的运算符。 格式:数据类型 变量名称 =条件判断 ? 表达式a :表达式b int a=10; ...

Zzh1110的博客 1万+

JavaSE 总复习:语法到多线程全梳理

JavaSE 高效复习指南(150字摘要) 本文提出结构化复习JavaSE的方法,强调从问题解决角度切入而非死记API。将JavaSE划分为12个核心模块,每个模块明确其解决的问题:基础语法构建代码基础,面向对象组织代码结构,集合管理数据,IO处理文件读写,多线程实现并发等。复习重点包括:掌握最小实现代码、识别常见错误、设计自测方案。特别提醒关注面向对象、集合、IO和多线程等核心模块,同时注意JDK版本差异。文章采用"问题导向+代码实践"的方式串联知识点,并提供详细笔记链接供深入查阅。 (150字)

一个持续进化的java+ai学习者博客:AI Agent 工具链(Codex、Harness、Claude Code、MCP、Skills)实战笔记 + 后端开发教程,每周稳定更新。 4888

JavaSE多线程笔记

javase多线程笔记

cc_1113的博客 916

[JavaSE] 学习路线 4周时间规划

文章目录知识点:练习题 知识点: 2019-6-21 [JavaSE] 背景.特点.安装.DOS.环境变量.工作原理.进制转换 2019-6-22 [JavaSE] 注释.变量.数据类型.整数.浮点数.字符.布尔.类型转化.运算符 2019-6-24 [JavaSE] 算术运算符 关系运算符 逻辑赋值运算符 条件运算符 位运算符 运算符优先级 控制台输入 流程控制语句 分支结构 2019-6-25...

景子墨的博客 1896

JavaSE(多线程和线程池)知识点总结

​ 线程就是操作系统能够进行运算调度的最小单位,它被包含在进程(进程是程序的基本执行实体,任务管理器种选择的进程就可以看到每个软件的进程)中,是进程中实际运作的单位。​ 比如:360杀毒软件,包括木马查杀、电脑清理、系统修复功能。其中360杀毒软件就是进程,其中的功能就是线程。​ 简单理解:应用软件种相互独立,可以同时运行的功能就是线程。

weixin_46119796的博客 1745
上一篇: 回溯算法解决问题小套路总结
下一篇: 动态规划0-1背包基本介绍
Xiao艾扶
博客等级 码龄7年 187粉丝 93原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao艾扶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值