对线程的一点认识

本文深入讲解Java线程的创建方式,包括实现Runnable接口与继承Thread类两种方法,并介绍线程的状态变化及线程间的通信机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程

1.创建线程

Java使用下列类/接口实现线程功能:

Runnable接口

Thread

ThreadGroup

例如使用Runnable接口实现线程功能:

创建的线程类

Class WorkThread implements Runnable{

Public void run(){

....................

}

}

在创建这个线程类之后,需要创建Thread类的实例,然后将WorkThread类的对象作为参数传递给Thread类的对象作为参数传递给Thread类的构造函数,如下:

Thread t = new Thread(new WorkThread())

创建的线程会执行WorkThread类定义的run方法。当run方法执行完之后,此线程就死亡了,并且不能被重新安排另一次运行。

另外可以通过子类化Thread类来创建线程:

Class WorkerThread extends Thread{

  Public void run(){

................

}

}

通过重写run方法来实现自己期望的功能。

Thread t = new WorkerThread();

T.start();

在线程启动时,并不意味着线程会立即获得CPU。相反,线程会被放入准备运行队列中,之前讨论过,线程最终会获得CPU时间片来执行。

注意

实现Runnable接口被认为是一种面向对象的方法,这种方法被推荐使用,优于通过子类化Thread类的方法。而且,如果你的类已经扩展了其他类,就不允许同时扩展Thread类。

ThreadGroup类允许将所有逻辑上相关的线程归到组中,这样你就能同时改变属于某个组的所有线程。默认情况下:创建的所有线程属于同一个组。然而,也可以创建自己的其他组,然后向其中添加新创建的线程。线程组可能包含其他的线程组,因此,可以为线程构建树型层次结构。

1.新建状态

线程在已经利用new关键字创建但是还未执行的这段时间里,处于一种特殊的新建状态中,此时,线程对象已经被分配了内存空间,私有数据已经被初始化,但是该线程尚未被调度。此时的线程可以被调度,变成可运行状态,也可以被杀死,变成死亡状态。

2.就绪状态

在处于创建状态的线程中调用start()方法将线程的状态转换为就绪状态。这是,线程已经得到除CPU时间之外的其他系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会

3.运行状态表明线程正在运行,该线程已经拥有了对CPU的控制权。这个线程一直运行到运行完毕,除非该线程主动放弃CPU的控制权或者CPU的控制权被优先级更高的线程抢占,处在运行状态的线程在下列情况下将让出CPU的控制权:

(1)线程运行完毕;

(2)有比当前线程优先级更高的线程进入可运行状态;

(3)线程主动睡眠一段时间;

(4)线程在等待某一资源。

4.如果一个线程处于挂起状态,那么这个线程暂时无法进入就绪队列。处于挂起状态的线程通常需要由某些事件才能唤醒,至于由什么事件唤醒该线程,则取决于其挂起的原因。处于睡眠状态的线程必须被挂起一段固定事件,当睡眠事件结束时就可以变成运行状态;因等待资源或消息而被挂起的线程则需要由一个外来事件唤醒。

5.正常情况下run()返回使得线程死亡。调用stop()destroy()亦有同样的效果,但是不被推荐,因为前者会产生异常,后者是强制终止,不会释放内存。

方法

        说明

有效状态

目标状态

Stat()

开始执行线程

新建

运行

Stop()

终止执行线程

运行

死亡

Sleep()

睡眠指定毫秒长的时间

运行

就绪

Sleep()

睡眠指定长的时间,允许纳秒级控制

运行

就绪

Suspend()

挂起线程

运行

就绪

Resume()

继续执行

就绪

运行

Yield()

暂时释放线程

运行

运行

Join()

调用该方法的线程转为挂起状态,直到本线程进入死亡状态才再次进入可运行状态

运行

挂起

线程同步是指java避免多个线程同时访问一个数据而造成数据混乱的方法。它可以避免多个线程同时访问相同的数据时,产生线程之间的争抢,避免一个线程刚生成的数据又会被其他线程生成的数据所覆盖。

Java用监听器的手段来完成线程的同步,就好像监听器把受保护的资源外面添加了一把锁,而这把锁只有一把钥匙。每个线程只有在得到这把钥匙之后才可以对被保护的资源执行操作,而其他的线程只能等待,直到能拿到这把钥匙。

方法同步:

一个类中任何方法都可以设计成为synchronized方法,以防止多线程数据崩溃。当一个线程进入synchronized方法后,能保证在其他任何线程访问这个方法之前完成自己的一次执行。如果一个线程试图访问一个已经启动的synchronized方法,则这个线程必须等待,直到已启动线程执行完毕,释放这个synchronized方法后才能访问。

例子:

public class DepositThread implements Runnable{

Account acc;

private static int NUM_OF_THREAD = 100;

static Thread[] threads = new Thread[NUM_OF_THREAD];

public DepositThread(Account acc){

this.acc = acc;

}

@Override

public void run() {

// TODO Auto-generated method stub

acc.deposit(20.0f);

acc.withdraw(10.0f);

}

public static void main(String[] args) {

final Account acc = new Account("王红", 1000.0f);

for(int i = 0; i < NUM_OF_THREAD; i++){

DepositThread my = new DepositThread(acc);

threads[i] = new Thread(my);

threads[i].start();

}

for(int i = 0; i < NUM_OF_THREAD; i++){

try {

threads[i].join();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

System.out.println("完成,王红的账户余额为:" + acc.getBalance());

}

}

class Account{

String name;

float amount;

public Account(String name, float amount){

this.name = name;

this.amount = amount;

}

public synchronized void deposit(float amt){

float tmp = amount;

tmp += amt;

try {

Thread.sleep(1);

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

amount = tmp;

}

public synchronized void withdraw(float amt){

float tmp = amount;

tmp -= amt;

try {

Thread.sleep(1);

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

amount = tmp;

}

public float getBalance(){

return amount;

}

}

方法同步和对象同步有同样的效果。

例如:

Public synchronized void yourMethod(){

...................

}

与下面代码效果相同

Public void yourMethod(){

Synchronized(this){

........

}

}

区别:有时,一个方法执行时间很长,而其中只有很短的一段时间访问关键数据,在这种情况下,将整个方法声明为synchronized,将导致其他线程因无法调用该线程的synchronized方法进行操作而长时间无法继续执行,这在整体效率上是不划算的。此时,就可以使用对象同步,只把访问关键数据的代码段用花括号括起来,在其前加上synchronized(this)即可。

线程组的各种方法:

public class TheadGroupTest {

public void test(){

ThreadGroup tg = new ThreadGroup("test");

Thread A = new Thread(tg,"A");

Thread B = new Thread(tg, "B");

Thread C = new Thread(tg, "C");

A.setPriority(6); //设置线程A的优先级为6

C.setPriority(4); //设置线程C的优先级为4,线程B的默认为5

System.out.println("tg线程组正在活动的线程个数:" + tg.activeCount());

System.out.println("线程A的优先级是:" + A.getPriority());

//设置线程组tg的优先级,但是不能改变A,B,C各自的优先级

tg.setMaxPriority(8);

System.out.println("tg线程组的优先级是:" + tg.getMaxPriority());

System.out.println("tg线程组上一级线程组信息:" + tg.getParent());

System.out.println("线程组名称:" + tg.getName());

System.out.println("tg线程组的信息:");

tg.list(); //对每个线程组中的线程调用toString()方法,返回线程组的名称和优先级

}

public static void main(String[] args) {

TheadGroupTest ttg = new TheadGroupTest();

ttg.test();

}

}

线程之间的通信:

多线程的一个重要特点是它们之间可以互相通信,线程通信使线程之间可以相互交流和等待,可以通过经常共享的数据使线程相互交流,也可以通过线程控制方法使线程相互等待。Object类为此提供了3个方法:wait(),notify(),notifyAll()。

Wait()使当前线程处于等待状态。

Notify()唤醒等待该对象线程

NotifyAll()唤醒所有等待该对象的所有线程

典型的线程间通信建立在生产者和消费者模型上:一个线程产生输出(相当于生产产品),另一个线程使用输入(相当于消费产品),先有生产者生产,才能有消费者消费。生产者未生产之前,通知消费者等待;生产后通知消费者消费,消费者消费以后再通知生产者生产,这就是等待通知机制。

代码如下:

public class ProducerThread {

public static void main(String[] args) {

Store s = new Store();

Producer pd = new Producer(s);

Consumer cs = new Consumer(s);

new Thread(pd).start();

new Thread(cs).start();

}

}

//仓库

class Store{

int product = 0;

boolean bfull = false;   //仓库中是否有产品的判断条件,初始条件是没有产品

public synchronized void put(int product){

if(bfull){

try {

wait();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

this.product = product;

System.out.println("生产者生产的产品:" + product);

bfull = true;

notify();

}

public synchronized void get(){

if(!bfull){

try {

wait();

catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

System.out.println("消费者消费的产品:" + product);

bfull = false;

notify();

}

}

class Producer implements Runnable{

Store s;

public Producer(Store s){

this.s = s;

}

@Override

public void run() {

// TODO Auto-generated method stub

for(int  i = 1; i <= 10; i++){

s.put(i);

}

}

}

class Consumer implements Runnable{

Store s;

public Consumer(Store s){

this.s = s;

}

@Override

public void run() {

// TODO Auto-generated method stub

for(int i = 1; i <= 10; i++){

s.get();

}

}

}

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用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、付费专栏及课程。

余额充值