多线程

一:进程概述及多进程的意义

              进程概述:进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的

                                内存空间和系统资源。

              多进程的意义:多进程的意义单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏

                                     (游戏进程一边听 乐(音乐进程),所以我们常见的操作系统都是多进程操作系统。

                                     比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务.

                                     对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。

                                     因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换.

                                    且切换速度很快, 所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。 多进程的作用不是                                        提高执行速度,而是提高CPU的使用率。

二:线程概述及多线程的意义

               线程:在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。

               多线程有什么意义:多线程的作用不是提高执行速度,而是为了提高应用程序的使用率. 那么怎么理解这个问题呢?
                                             我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到
                                             CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序
                                             中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们
                                             中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.

               Java程序运行原理和JVM的启动是多线程的吗?

               Java程序运行原理:
                         Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。
                         该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。
               JVM的启动是多线程的吗:      

                         JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

 

 1.  java中的多线程:Thread类

          创建线程:第一种方法

               Thread t = new Thread() {
                        public void run() { // 运行
                         // 线程需要执行的代码
                         // 不能抛出检查异常,只能自己处理检查异常
                 }
           };
          直接调用run和使用start间接调用run的区别:
                     直接调用run是在主线程中执行了run,没有启动新的线程
                     使用start是启动新的线程,通过新的线程间接执行run

package ogr.westos.Demo;

public class Demo1 {
    public static void main(String[] args) {
        //创建一个线程
        Thread thread = new Thread(){
            public void run(){
                System.out.println("线程thread开始运行");
            }
        };

        thread.start();//启动线程
        System.out.println("主线程开始运行");
    }
}

创建线程的第二中方法:

          把线程和要执行的代码分开
          Thread 代表线程
          Runnable 可运行的(线程要执行的代码)

package ogr.westos.Demo;

public class Demo2 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable(){
            public void run(){
                System.out.println("线程所需要执行的代码");
            }
        };

        Thread thread = new Thread(runnable);//创建一个线程,把包装需要执行代码的runnable,传入此线程中
        thread.start();//线程开始
        System.out.println("主线程开始运行"); 
    }
}

  2. 线程中常见的方法
                Thread.sleep(long n); // 让当前线程休眠n毫秒
                Thread.currentThread(); // 找到当前线程                                                                                                                                           join(long n) 表示等待线程结束,但是最多等待n毫秒                                                                                                                          start() 让线程启动, 只能调用一次,如果调用了多次会出现IllegalThreadStateException

      其它方法:
                getName()  得到线程的名称
                yield()      谦让

      不推荐使用的方法
               .stop() 让线程停止
               .suspend() 让线程暂停
               .resume() 让线程继续

线程中常见方法的举例:

package ogr.westos.Demo;

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread()+"开始");
                try {
                    System.out.println("线程一开始休眠");
                    Thread.sleep(1000);//线程休眠一秒
                    System.out.println("线程一结束");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println("主线程开始");
        t1.start();//线程1开始
        t1.join(2000);//等待线程1结束,最多等两秒
        System.out.println("主线程结束");
        }
}

3.守护线程:

              java进程需要等待所有线程都运行结束,才会结束.
             有一种特殊的线程叫做守护(守护的是主线程)线程,只要主线程运行结束,即使守护线程的代码没有执行完,                          也会跟着主线程一起结束

守护线程举例:

package ogr.westos.Demo;

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            System.out.println("守护线程开始");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("守护线程结束");
        });

        t1.setDaemon(true);
        t1.start();
        Thread.sleep(5000);
    }
}

4.线程的并发(Concurrent)

          判断一个多线程应用程序是否有问题的标准:
                 是否是多线程环境
                 是否存在共享数据
                 是否存在多条语句同时操作共享数据

          synchronized (同步关键字)

       解决这一安全问题:     需要使用同步代码块:
           格式:   
                  synchronized(对象){//不能在括号了直接new 对象 new 了 就没效果
                   要被同步的代码

                    }
                  这个同步代码块保证数据的安全性的一个主要因素就是这个对象
                  注:这个对象 要定义为静态成员变量 才能被所有线程共享
                  需要这个对象被所有的线程对象所共享,这个对象其实就是一把锁.这个对象习惯叫做监视器

                  每个对象都有一个自己的monitor(监视器),当一个线程调用synchronized(对象),就相当于进入了这

                  个对象的监视器。要检查有没有owner,如果没有,此线程成为owner; 但如果已经有owner了,这个线程在entryset

                  的区域等待  owner的位置空出来.在竞争的时候,是非公平的synchronized必须是进入同一个对象的monitor
       

                   同步的好处:    同步的出现解决了多线程的安全问题。
                   同步的弊端:   当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行                                          效率。

举例:

package ogr.westos.Demo;

public class Demo6{
        static int i = 0;
        static Object obj = new Object(); //创建一个对象,这个对象被共享,作为线程在执行过程中的监视器(锁)

        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(() -> {//线程一
                for (int j = 0; j < 5000; j++) {
                    synchronized (obj) { // 线程一进锁
                        i++;
                    }//线程执行完后,线程1出锁
                }
            });

            Thread t2 = new Thread(() -> { // 乙
                for (int j = 0; j < 5000; j++) {
                    synchronized (obj) {//线程一如果进锁,则线程二在外面等待,等线程一出锁猜可以进入
                        i--;
                    } //
                }
            });
            t1.start();
            t2.start();

            t1.join();
            t2.join();
            System.out.println(i);
        }
}

5.volatile 易变的

               可以用来修饰成员变量和静态成员变量,他可以防止线程从自己的高速缓存中查找变量的值,必须到主存中获取它的                    值。它保证的是变量在多个线程之间的可见性, 不能保证原子性

package ogr.westos.Demo;
//一个线程对run变量的修改对于另一个线程不可见,导致了另一个线程无法停止
public class Demo7 {

        volatile static boolean run = true;

        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(()->{
                while(run){
                    // ....
                }
            });
            t.start();

            Thread.sleep(1000);
            run = false;
        }
    }

             一个线程对run变量的修改对于另一个线程不可见,导致了另一个线程无法停止

             synchronized 语句块既可以保证代码块的原子性,也可以保证代码块内变量的可见性。

             但缺点是synchronized是属于重       量级操作,性能会受到影响。

6.synchronized的另外两种写法

1).synchronized关键字加在方法上:

public synchronized void test() {

}
等价于
public void test() {
    synchronized(this) {

 }
}
```
2).synchronized关键字加在静态方法上:
class Test{
    public synchronized static void test() {

    }
}
等价于
public static void test() {
    synchronized(Test.class) {
        
    }
}


 同步代码块的锁对象:     任意一个对象
 同步方法的锁对象:       是this
 静态同步方法的锁对象:就是当前类对应的字节码文件对象

7:线程死锁

         如果出现了同步嵌套,就容易产生死锁问题
         是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
         死锁: 两个或者两个以上的线程,在抢占CPU的执行权的时候,都处于等待状态

​
package ogr.westos.Demo;
public class Demo8 {
    static Object obj1 = new Object();
    static Object obj2 = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程1开始");
                synchronized (obj1){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程一进入obj1锁");
                    synchronized (obj2){
                        System.out.println("线程一进入obj2锁");
                    }
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程2开始");
                synchronized (obj2){
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("线程2进入obj2锁");
                    synchronized (obj1){
                        System.out.println("线程2进入obj1锁");
                    }
                }
            }
        });

        t1.start();
        t2.start();

    }
}

​

7. wait() notify() notifyAll()都属于Object对象的方法

         wait() 等待
         notify() 唤醒
         它们都是线程之间进行协作的手段

         obj.wait(); 让object监视器的线程等待
         obj.notify(); 让object上正在等待的线程中挑一个唤醒 
         obj.notifyAll(); 让object上正在等待的线程全部唤醒                                    

         必须获得此对象的锁,才能调用这几个方法

package ogr.westos.Demo;

public class Demo9 {
    public static void main(String[] args) {
        Object obj = new Object();

        new Thread(() -> {
            synchronized (obj) {
                System.out.println("thread-0线程执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread-0其它代码....");
            }
        }).start();

        new Thread(() -> {
            synchronized (obj) {
                System.out.println("thread-1线程执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread-1其它代码....");
            }
        }).start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("唤醒obj上其它线程");
        synchronized (obj) {
//            obj.notify();
            obj.notifyAll();
        }

    }
}

wait() 方法实际是会释放对象的锁,进入WaitSet等待区,从而让其他线程就机会获取对象的锁。无限制等待,直到notify为止wait(long n)  有时限的等待, 到n毫秒后结束等待,或是被notify

sleep(long n), wait(long n)两者之间的区别:

         1) sleep是Thread方法,而wait是Object的方法
         2) sleep不需要强制和synchronized配合使用,但wait需要和synchronized一起用
         3 )   sleep 在睡眠的同时,不会释放对象锁的,但wait在等待的时候会释放 

8.线程的状态:

         NEW(新建)  线程刚被创建,但是还没有调用 start方法

         RUNNABLE(可运行) 当调用了start() 方法之后

         BLOCKED(阻塞) 当线程进入了monitor监视器区,处于entrySet里准备竞争锁的时候,处于阻塞状态

         WAITING(等待) 当调用了对象的wait方法,或调用了线程对象的join方法,进入了WaitSet,处于等待状态

         TIMED_WAITING 当调用wait(long n) join(long n) 进入了WaitSet,处于有限时的等待状态
         当调用sleep(long n) 是让当前线程放弃cpu的时间片,睡眠一会

         TERMINATED (终止)当线程代码运行结束.

9. java.util.concurrent.* java并发工具包

10.第三种创建线程的方式

             public interface Callable<V> 

             与Runnable的区别,1)有返回结果,2)可以抛出检查异常

 

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值