Java学习之多线程的总结

多线程总结:

1.多线程的实现

方式1: 使用一个继承了 Tread类并重写其run方法的自定义类来实现
MyThread myThread = new MyThread();

启动线程中的run方法:
myThread.start();

自定义类:
class MyThread extends Thread{
    @Override
    public void run() {
        
            System.out.println("这是一个run方法");
    }
}


方式2: 使用一个实现了 Runnable 接口并重写其run方法的自定义类来实现
    
创建实现类对象:
RunnableThread runnableThread = new RunnableThread();

将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象通过Thread类的对象调用start()
new Thread(runnableThread,"线程").start();

自定义类:
static class RunnableThread implements Runnable{
        int Num = 100;
        @Override
        public void run() {
            
              System.out.println("这是一个run方法");
        }
    }

相较于方式1的优点:
    该方式构建线程时,所构建的多个线程会共用一个Runnable接口的子实现类对象,若该类中定义有变量,这样就可以实现这些进程共享同一个变量

    
方式3: 使用Callable方式实现多线程

首先创建一个线程池对象
ExecutorService executorService = Executors.newFixedThreadPool();

在submit函数中传递一个Callable的匿名内部类作为传递参数;其中重写的call方法相当于run方法,并且还需要一个返回值
executorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                System.out.println("这是一个call方法");
                return null;
            }
        }); 
    

方式4: 使用匿名内部类的方式实现多线程
        new Thread(){
                    @Override
                    public void run() {
                        System.out.println("这是一个run方法");
                    }
                }.start();

		Runnable的匿名内部类作为参数传递到Thread的构造方法中实现对start()的调用
        new Thread(new Runnable(){
            @Override
            public void run() {
                    System.out.println("这是一个run方法");
            }
        }).start();

2.获取线程信息、自定义线程名称

获取线程信息:
Thread thread = Thread.currentThread(); // 获取当前正在运行的线程对象
System.out.println("当前线程名称:" + thread.getName());
System.out.println("当前线程ID:" + thread.getId());
System.out.println("当前线程状态:" + thread.getState());  // RUNNABLE 表示正在运行
System.out.println("当前线程优先权:" + thread.getPriority());

自定义线程名称:

首先使用匿名类通过构造器传入自定义的线程名称
new ThreadName("李玉龙").start();

 static class ThreadName extends Thread {

        //super()调用父类的构造方法
        public ThreadName(String name) {
            super(name);
        }

        @Override
        public void run() {
            System.out.println("这是一个run方法");
            }
        }

3.设置线程优先权

//设置优先权
注:
    1.优先级设置时需要在1-10
    2.值越大优先级越高
    3.线程的执行具有一定的随机性,优先权高的线程不一定先执行,优先权高只表明线程获取到CPU的执行权的可能性更高

PriorityThread thread1 = new PriorityThread("张三");
thread1.setPriority(1);
PriorityThread thread2 = new PriorityThread("李四");
thread2.setPriority(2);

 static class PriorityThread extends Thread{
        public PriorityThread(String name) {
            super(name);
        }
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("当前线程名称:" + this.getName());
                System.out.println("当前线程优先权:" + this.getPriority());
            }
        }
    }


4.线程控制

控制方式:
① sleep 可以使当前的线程处于睡眠状态(具有倒计时的阻塞状态)
    sleep(1000)  => 每次执行当前线程睡眠1sleep(long millis, int nanos)  => 按毫秒+纳秒方式睡眠
    
    try {
        Thread.sleep(1000);
     } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }


② join 当使用该方法时,其他线程必须等待当前线程执行完成才能继续执行
    
ControlThread thread1 = new ControlThread("李白");
        thread1.start();
        thread1.join();yield 礼让线程,让当前线程退出CPU的执行权,使得所有线程重新争夺CPU资源
     yield();


④ setDaemon 设置当前线程为后台守护线程,只要当前存在任何一个非守护线程没有结束,守护线程就必须工作;只有当最后一个非守护线程结束时,守护线程才会随着一同结束工作。
    
DaemonThread thread = new DaemonThread("张三");
     thread.setDaemon(true);
     thread.start();
    

⑤ 中断线程
public final void stop()  强制停止  => 该方法已经过时了
public void interrupt() 非强制执行,在当前线程在本次CPU执行时间片断执行完成后,再进行关闭操作
  会有异常提示信息
    
 if ("邱六".equals(this.getName()) && i == 3){
 //强制终止
	   this.stop();
       this.interrupt();//中断后会显示一个错误信息,但是并不影响程序的后续执行
         }   

5.对共享变量加锁

对于多个线程使用同一个变量所造成的线程不安全的问题,我们可以通过对共享变量上锁来进行解决
 
1.对当前对象进行上锁
    
sellRunnable对象是 全局唯一的 ,并且其中的变量ticksNum也是唯一的;因此可以对当前对象进行上锁
        SellRunnable sellRunnable = new SellRunnable();
        new Thread(sellRunnable, "售票员1").start();
        new Thread(sellRunnable, "售票员2").start();

static class SellRunnable implements Runnable {
        int ticksNum = 100;

        @Override
        public void run() {
            // TODO 一旦当线程获取到唯一的一个this对象,其他线程如果想要执行同步代码块,必须要等当前线程释放

            // 同步代码块
            while (true) {
                //synchronized对当前SellRunnable的对象进行上锁
                synchronized (this) {
                    // TODO  this表示当前 SellRunnable 的对象,由于在main方法中只有一个sellRunnable对象,故此可以锁住该对象实现共享变量的互斥
                    对共享变量的操作
                }
            }
        }   
    }


2.对类进行上锁,类中的变量为静态变量

new SynchronizedClazz("销售员1").start();
new SynchronizedClazz("销售员2").start();

static class SynchronizedClazz extends Thread{
        public SynchronizedClazz(String name) {
            super(name);
        }

        @Override
        public void run() {

                // TODO 对TicksNumClazz静态内部类进行加锁操作,在同一时刻,有且只能使用一次该类
                synchronized (TicksNumClazz.class) {
                   if (TicksNumClazz.getNum() > 0) 
                   对共享变量进行操作
                }
        }
}

static class TicksNumClazz {
        static int num = 100;
        public static void sellTick() {
            num -= 1;
        }

        public static int getNum() {
            return num;
        }
    }

3.对类进行上锁,类中的变量不为静态变量
    2中的写法,使用的是静态变量
    在有些场合下,不是很方便
    
TicksNum ticksNum = new TicksNum();
TicksNumObject ticksNumObject = new TicksNumObject(ticksNum);
new SynchronizedClazz("销售员1",ticksNumObject).start();
new SynchronizedClazz("销售员2",ticksNumObject).start();

static class SynchronizedClazz extends Thread{
        TicksNumObject ticksNumObject;
    
        public SynchronizedClazz(String name,TicksNumObject ticksNumObject) {
            super(name);
            this.ticksNumObject = ticksNumObject;
        }

        @Override
        public void run() {
            while (true){
                // TODO 针对TicksNumObject使用加锁操作 表示在同一时刻 有且只能使用一次该类
                synchronized (TicksNumObject.class) {
                   if (ticksNumObject.ticksNum.getNum() > 0)
                    对共享变量进行操作
                    }
                }
            }
        }

static class TicksNumObject {
        //TODO 更改其类内部的变量、方法,使其不是静态的;并将原类中的变量num通过一个类来进行实现
        TicksNum ticksNum;
        public TicksNumObject(TicksNum ticksNum) {
            this.ticksNum = ticksNum;
        }
    }

static class TicksNum{
        //使得num成为另外一个类中的属性
        int num = 100;
        public void sellTick() {
            num -= 1;
        }
        public int getNum() {
            return num;
        }
    }

6.线程同步

synchronized 也可以对当前成员方法进行加锁,加锁的是当前对象
    public synchronized void sell()synchronized 也可以对当前静态方法进行加锁,加锁的是当前方法所属的类 
    public static synchronized void sell()
    
    
③ notify 作用是从当前对象被锁定的线程中唤醒一个线程; notifyAll的作用是幻想所有线程
④ wait 作用是让当前线程进入阻塞状态,并释放并释放对象的锁。如果要被唤醒,那么必须有一个线程使用 notify 
⑤ notify和wait必须在synchronized修饰的代码块或者方法中才能存在
⑥ notify和wait是属于顶级父类Object中的方法
⑦ notify和wait 调用的对象,必须是被加锁的
    
    try {
                tickNum.notify(); // 唤醒其他线程
//                tickNum.notifyAll(); // 唤醒所有线程
                tickNum.wait();  // 对当前的对象进行阻塞

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

7. Lock锁

Lock锁,可以得到和 synchronized一样的效果,即实现原子性、有序性和可见性。
相较于synchronizedLock锁可手动获取锁和释放锁、可中断的获取锁、超时获取锁。
    
具体实现:
 // TODO 相当于在当前位置添加了一个 synchronized 同步锁,对tickNum进行上锁,操作完成后进行解锁
		lock.lock();
        if (tickNum > 0) {
             sell();
         } else {
          break;
         }
        lock.unlock();

8. 线程组

ThreadGroup threadGroup1 = new ThreadGroup("帅哥组");

MyThread thread3 = new MyThread(threadGroup1, "雍正");
MyThread thread4 = new MyThread(threadGroup1, "康熙");
thread3.start();
thread4.start();	

 static class MyThread extends Thread {
        public MyThread(ThreadGroup group, String name) {
            super(group, name);
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if (i >= 10 && this.getThreadGroup().getName() == "美女组") {
                    this.getThreadGroup().stop();  // 可以将整个线程组的线程全部关闭
                }
                System.out.println(this.getThreadGroup().getName()+"线程名称:"+this.getName() + "正在打印第" + i + "个值");
            }
        }
    }

9. 线程池

//newCachedThreadPool()方法可以创建一个可缓存线程池;线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

ExecutorService executorService1 = Executors.newCachedThreadPool();
executorService1.submit(new MyThreadRunnable());  // 当线程池中给定了线程对象那么就可以执行线程

executorService1.shutdownNow();  // 使得线程池停止接收新任务,原来的任务停止执行
executorService1.shutdown(); // 线程池停止接收新任务,原来的任务继续执行

//newFixedThreadPool(int n)方法会创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(new MyThreadRunnable());
executorService.submit(new MyThreadRunnable());
executorService.submit(new MyThreadRunnable());
executorService.submit(new MyThreadRunnable());

执行结果:
             /*
                pool-1-thread-3当前线程正在执行..0
                pool-1-thread-2当前线程正在执行..0
                pool-1-thread-1当前线程正在执行..0
                pool-1-thread-2当前线程正在执行..1
                pool-1-thread-1当前线程正在执行..1
                pool-1-thread-3当前线程正在执行..1
             */

10. Timer的任务调度

我们可以给定一个执行的策略,来操控任务的执行,比如每5秒执行一次

具体实现:
    
    //schedule(new MyTask(),1000,2000)第一个参数给定线程运行的对象,第二个参数是延迟时间,第三个参数是间隔时间,表示第一次调用之后,从第二次开始每隔多长的时间调用一次 run() 方法
    
    Timer timer = new Timer();
    timer.schedule(new MyTask(),5000);
    timer.schedule(new MyTask(),1000,4000);

static class MyTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("定时炸弹被引爆,pong....");
        }

    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入解析算法效率核心:时间与空间复杂度概览及优化策略

算法复杂度&#xff0c;即时间复杂度与空间复杂度&#xff0c;衡量算法运行时资源消耗。时间复杂度反映执行时间随数据规模增长的关系&#xff0c;空间复杂度表明额外内存需求。优化策略&#xff0c;如选择合适数据结构、算法改进、循环展开等&#xff0c;对于提升程序效率、减…

从抖音阳哥的经验看,选品师项目是否值得你投入?

在抖音这个短视频平台上&#xff0c;无数的创作者分享着他们的知识和经验&#xff0c;其中阳哥作为一个备受关注的博主&#xff0c;他的每一次分享都能引起广大网友的热烈讨论。最近&#xff0c;阳哥分享了一个名为“选品师”的项目&#xff0c;让许多对电商和抖音运营感兴趣的…

RK3576 Android平台SD启动

RK3576 Android平台SD启动 要求 1&#xff0c;Android14及以上版本&#xff1b;2&#xff0c;SD卡制作工具&#xff1a;瑞芯微创建升级磁盘工具V1.78及以上版本&#xff1b;3&#xff0c;SD卡容量8G-32G之间&#xff1b; 软件修改 1&#xff0c;Android部分 修改PRODUCT_BO…

Android 的 Timer 和 TimerTask

Timer 简介(来自Gemini) Timer 是 Java 中用于创建定时任务的类。它位于 java.util 包中。可以使用 Timer 来安排一次性或定期执行的任务。 每个 Timer 对象都对应一个后台线程。此线程负责从任务队列中检索任务并按计划执行它们。 使用 Timer 要使用 Timer&#xff0c;首先…

3D 渲染至少需要多少显存?显存真得越大越好吗?

无论是电影制作、游戏开发还是建筑效果图设计&#xff0c;在今天的计算机图形学领域&#xff0c;都需要强大的图形处理能力。而显存&#xff08;VRAM&#xff09;作为支持这一切的重要组成部分&#xff0c;对于高效3D渲染同样至关重要。在本文中&#xff0c;我们将一起探讨显存…

Cmake-learning

可以把cmake看成一款自动生成 Makefile的工具&#xff0c;所以编译流程就变成了&#xff1a;cmake—>make–>可执行文件 在哪个目录执行cmake txt位置 就会在哪个目录生成构造文件和可执行文件 project(HELLO): 非强制&#xff0c;指明项目名称 add_executable(he…

【算法】滑动窗口——水果成篮

本篇博客是我对“水果成篮”这道题由暴力解法到滑动窗口思路的具体思路&#xff0c;有需要借鉴即可。 目录 1.题目2.暴力求解3.暴力优化3.1每次right不用回退3.2有些left长度一定不如前一个&#xff0c;不用走&#xff0c;left不回退 4.滑动窗口算法5.总结 1.题目 题目链接&am…

怎么用AI软件设计字体

一 工具准备 软件&#xff1a;Adobe illustrator 如下网盘自取 链接&#xff1a;https://pan.baidu.com/s/1hlImpN4QlsSkOLLUxINOGA 提取码&#xff1a;love 安装的时候看不全界面&#xff0c;多按几下tab键就能看到按钮。 直接找一款喜欢的字体修改&#xff0c;字体包如下…

物联网网关制造生产全流程揭秘!

如果您正有开发和定制物联网网关的计划&#xff0c;找一个专业的物联网设备厂商协助您制造生产物联网网关可以节省大量时间和成本&#xff0c;可以让您能专注于当前核心业务&#xff0c;而无需将精力过多地投入到自己不擅长的领域。 当然&#xff0c;了解物联网网关的测试和制…

基于JSP动漫论坛的设计与实现(二)

目录 3. 系统开发环境及技术介绍 3.1 开发环境 3.2 开发工具 3.2.1 MyEclipse8.5 3.2.2 MySql 3.3 相关技术介绍 3.3.1 JSP技术简介 3.3.2 JDBC技术技术简介 3.3.3 MVC模式与Struts框架技术 4. 总体设计 4.1 系统模块总体设计 4.1.1 普通用户模块设计 4…

数据库的使用基础-SQL语句

一、在MYSQL中&#xff0c;创建数据库&#xff0c;语法如下&#xff1a; CREATE DATABASE [IF NOT EXISTS] <数据库名> [[DEFAULT] CHARACTER SET <字符集名>] [[DEFAULT] COLLATE <校对规则名>];[ ]中的内容是可选的。语法说明如下&#xff1a; <数据库…

LeetCode738:单调递增的数字

题目描述 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 332 代码 class Solution { public:int monotoneIncreasingDigits(…

Imagine Flash、StyleMamba 、FlexControl、Multi-Scene T2V、TexControl

本文首发于公众号&#xff1a;机器感知 Imagine Flash、StyleMamba 、FlexControl、Multi-Scene T2V、TexControl You Only Cache Once: Decoder-Decoder Architectures for Language Models We introduce a decoder-decoder architecture, YOCO, for large language models, …

QT---day4事件

1、思维导图 2、 头文件 #ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> #include<QIcon> //图标类 #include<QLabel> //标签类 #include<QMovie> //动图类 #include<QLineEdit> //行编辑器类 #include<QPushButton> //按钮…

AJAX知识点(前后端交互技术)

原生AJAX AJAX全称为Asynchronous JavaScript And XML,就是异步的JS和XML&#xff0c;通过AJAX可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无需刷新就可获取数据。 AJAX不是新的编程语言&#xff0c;而是一种将现有的标准组合在一起使用的新方式 …

【进程等待】阻塞等待 | options非阻塞等待

目录 waitpid 阻塞等待 options&非阻塞等待 pid_t返回值 阻塞等待VS非阻塞等待 waitpid 回顾上篇&#xff1a; pid_ t waitpid(pid_t pid, int *status, int options); 返回值&#xff1a; 当正常返回的时候waitpid返回收集到的子进程的进程ID&#xff1b;如果设置了…

C++容器之vector类

目录 1.vector的介绍及使用1.1vector的介绍1.2vector的使用1.2.1 vector的定义1.2.2 vector iterator 的使用1.2.3 vector 空间增长问题1.2.4 vector 增删查改1.2.5vector 迭代器失效问题1.2.6 vector 在OJ中的使用。 2.vector深度剖析及模拟实现2.1 std::vector的核心框架接口…

金三银四面试题(二十五):策略模式知多少?

什么是策略模式 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;旨在定义一系列算法&#xff0c;将每个算法封装到一个独立的类中&#xff0c;使它们可以互换。策略模式让算法的变化独立于使用它们的客户端&#xff0c;使得客户端可以根据…

车载测试系列:入行车载测试分享

车载测试前景如何&#xff1f; 软件定义汽车时代的发展趋势&#xff0c;随着控制器自主开发力度的加强&#xff0c;作为V流程中必备环节&#xff0c;车载测试工程师岗位需求会越来越多&#xff1b;控制器集成化&#xff0c;功能集成程度越来越高&#xff0c;对于测试工程师的知…

3. 初探MPI——(非阻塞)点对点通信

系列文章目录 初探MPI——MPI简介初探MPI——&#xff08;阻塞&#xff09;点对点通信初探MPI——&#xff08;非阻塞&#xff09;点对点通信初探MPI——集体通信 文章目录 系列文章目录前言一、Non-blocking communications1.1 Block version1.2 Non-blocking version 二、准…
最新文章