java中的多线程
java 线程基本介绍
1、进程与线程的区别
进程是指一个内存中运行的应用程序,每个进程都有一块独立的内存空间,一个进程包含一到多个线程。
每个线程都有他所属的进程,每个线程也就是该进程的一条执行路径,线程之间是高频率快速轮换执行的,‘同时’执行只是给人的感觉。
2、Java当中线程一般有5中状态
创建状态:生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
就绪状态:当调用了线程的start方法,线程就进入就绪状态,调用start方法后线程不是立即执行的,只是开始排队等待执行了,具体什么时候执行得看CPU心情,当线程从等待或者休眠状态回来之后也是进入到就绪状态。
运行状态:开始运行run()函数的代码,这时候就是运行状态啦。
阻塞状态:线程正在运行的时候被暂停就是进入到阻塞状态,sleep,suspend,wait都可以使线程进入阻塞状态。
死亡状态:run()方法运行结束或者调用了线程的stop方法后,该线程就会死亡,对于已经死亡的线程无法使用start方法使其进入就绪状态。
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。
对于直接继承Thread的类来说,代码大致框架是:
class 类名 extends Thread{ 方法1; 方法2; … public void run(){ // other code… } 属性1; 属性2; … }
先看一个简单的例子:
package com.hxw.Threads; class ThreadTest { /** * 观察直接调用run()和用start()启动一个线程的差别 * @author HaiCheng * @param args * @throws Exception */ publicstaticvoid main(String[] args){ Thread r=new ThreadDemo("直接调用run执行"); r.run(); Thread t1=new ThreadDemo("线程一"); t1.start(); Thread t2=new ThreadDemo("线程二"); t2.start(); for(int i=0;i<10;i++){ System.out.println("主线程执行------>"+i); } } publicstaticclass ThreadDemo extends Thread{ private String ThreadName; public ThreadDemo(String s){ this.ThreadName=s; } @Override publicvoid run() { for (int i = 0; i < 10; i++) { System.out.println(this.getThreadName()+"执行------>"+i); } } public String getThreadName() { returnThreadName; } } }
run和start的区别
1) start:
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到spu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
2) run:
run方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
通过实现Runnable接口:
大致框架是:
class 类名 implements Runnable{ 方法1; 方法2; … public void run(){ // other code… } 属性1; 属性2; … }
来先看一个小例子吧:
/** * @author Rollen-Holt 实现Runnable接口 * */ class hello implements Runnable { public hello() { } public hello(String name) { this.name = name; } public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "运行 " + i); } } public static void main(String[] args) { hello h1=new hello("线程A"); Thread demo= new Thread(h1); hello h2=new hello("线程B"); Thread demo1=new Thread(h2); demo.start(); demo1.start(); } private String name; }
【可能的运行结果】:
线程A运行 0
线程B运行 0
线程B运行 1
线程B运行 2
线程B运行 3
线程B运行 4
线程A运行 1
线程A运行 2
线程A运行 3
线程A运行 4
关于选择继承Thread还是实现Runnable接口?
其实Thread也是实现Runnable接口的:
class Thread implements Runnable { //… public void run() { if (target != null) { target.run(); } } }
Thread和Runnable的区别:
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
/** * @author Rollen-Holt 继承Thread类,不能资源共享 * */ class hello extends Thread { public void run() { for (int i = 0; i < 7; i++) { if (count > 0) { System.out.println("count= " + count--); } } } public static void main(String[] args) { hello h1 = new hello(); hello h2 = new hello(); hello h3 = new hello(); h1.start(); h2.start(); h3.start(); } private int count = 5; }
【运行结果】:
count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1
count= 5
count= 4
count= 3
count= 2
count= 1
大家可以想象,如果这个是一个买票系统的话,如果count表示的是车票的数量的话,说明并没有实现资源的共享。
我们换为Runnable接口
class MyThread implements Runnable{ private int ticket = 5; //5张票 public void run() { for (int i=0; i<=20; i++) { if (this.ticket > 0) { System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--); } } } } public class lzwCode { public static void main(String [] args) { MyThread my = new MyThread(); new Thread(my, "1号窗口").start(); new Thread(my, "2号窗口").start(); new Thread(my, "3号窗口").start(); } }
【运行结果】:
count= 5
count= 4
count= 3
count= 2
count= 1
总结一下吧:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
所以,本人建议大家尽量实现接口。
/** * @author Rollen-Holt * 取得线程的名称 * */ class hello implements Runnable { public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { hello he = new hello(); new Thread(he,"A").start(); new Thread(he,"B").start(); new Thread(he).start(); } }
【运行结果】:
A
A
A
B
B
B
Thread-0
Thread-0
Thread-0
说明如果我们没有指定名字的话,系统自动提供名字。
提醒一下大家:main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实际在就是在操作系统中启动了一个进程。
二、>线程常用方法解析
1. 判断线程是否启动:
方法解析:在这里提一下上面的
currentThread()方法,返回对当前正在执行的线程对象的引用。
isAlive(),测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。
package com.hxw.Threads; /** * @author Rollen-Holt 判断线程是否启动 * */ class hello implements Runnable { publicvoid run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName()); } } publicstaticvoid main(String[] args) { hello he = new hello(); Thread demo = new Thread(he); System.out.println("线程启动之前---》" + demo.isAlive()); demo.start(); System.out.println("线程启动之后---》" + demo.isAlive()); System.out.println("线程启动之后后 ---》" + demo.isAlive()); System.out.println("线程启动之后后 ---》" + demo.isAlive()); System.out.println("线程启动之后后 ---》" + demo.isAlive()); System.out.println("线程启动之后后 ---》" + demo.isAlive()); System.out.println("线程启动之后后 ---》" + demo.isAlive()); System.out.println("线程启动之后后 ---》" + demo.isAlive()); System.out.println("线程启动之后后 ---》" + demo.isAlive()); } }
【运行结果】
线程启动之前---》false
线程启动之后---》true
线程启动之后后 ---》true
Thread-0
线程启动之后后 ---》true
线程启动之后后 ---》true
Thread-0
线程启动之后后 ---》true
Thread-0
线程启动之后后 ---》true
线程启动之后后 ---》false
线程启动之后后 ---》false
从上面的例子来看:确实是有主线程和子线程在运行的,而且主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。这个叫非守护线程。
上面的例子也表示出了在run方法执行完成后,线程就死亡了,只是由于主线程和子线程之间同步问题,如果想原文的那样在demo.start()后只打印一个alive()是有问题的。
2. 线程的强制执行:thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法即A.jion(),直到线程A执行完毕后,才会继续执行线程B。
t.join(); //使调用线程 t 在此之前执行完毕。
t.join(1000); //等待 t 线程,等待时间是1000毫秒
/** * @author Rollen-Holt 线程的强制执行 * */ class hello implements Runnable { public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { hello he = new hello(); Thread demo = new Thread(he,"线程"); demo.start(); for(int i=0;i<50;++i){ if(i>10){ try{ demo.join(); //强制执行demo() }catch (Exception e) { e.printStackTrace(); } } System.out.println("main 线程执行-->"+i); } } }
【运行的结果】:
main 线程执行-->0
main 线程执行-->1
main 线程执行-->2
main 线程执行-->3
main 线程执行-->4
main 线程执行-->5
main 线程执行-->6
main 线程执行-->7
main 线程执行-->8
main 线程执行-->9
main 线程执行-->10
线程
线程
线程
main 线程执行-->11
main 线程执行-->12
main 线程执行-->13
...
3.线程的休眠:sleep() 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),
sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
package com.hxw.Threads; import java.text.SimpleDateFormat; /** * @author Rollen-Holt 线程的休眠 * */ class hello implements Runnable { publicvoid run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + i+" "+(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new java.util.Date()))); } } publicstaticvoid main(String[] args) { hello he = new hello(); Thread demo = new Thread(he, "线程"); demo.start(); } }
【运行结果】:(结果每隔2s输出一个)
线程0 2014-08-16 06:06:21
线程1 2014-08-16 06:06:23
线程2 2014-08-16 06:06:25
4.线程的优先级:
与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行。
线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先级为5,主线程的优先级也是5。
在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同。
可以用下面方法设置和返回线程的优先级。
public final void setPriority(int newPriority) 设置线程的优先级。
public final int getPriority() 返回线程的优先级。
newPriority为线程的优先级,其取值为1到10之间的整数,也可以使用Thread类定义的常量来设置线程的优先级,这些常量分别为:Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,它们分别对应于线程优先级的1、5和10,数值越大优先级越高。当创建Java线程时,如果没有指定它的优先级,则它从创建该线程那里继承优先级。
/** * @author Rollen-Holt 线程的优先级 * */ class hello implements Runnable { public void run() { for(int i=0;i<5;++i){ System.out.println(Thread.currentThread().getName()+"运行"+i); } } public static void main(String[] args) { Thread h1=new Thread(new hello(),"A"); Thread h2=new Thread(new hello(),"B"); Thread h3=new Thread(new hello(),"C"); h1.setPriority(8); h2.setPriority(2); h3.setPriority(6); h1.start(); h2.start(); h3.start(); } }
【运行结果】:
A运行0
A运行1
A运行2
A运行3
A运行4
B运行0
C运行0
C运行1
C运行2
C运行3
C运行4
B运行1
B运行2
B运行3
B运行4
。
5. 线程的礼让: yield()
通过调用yield方法,线程可能自动的转发控制权给其他等待的线程,一般情况,在等待其他具有相同优先级的线程产生的某个结果是,线程会转让控制权。考虑如下情形,多线程情况下,有一个可读写文件由多个线程来读写操作,多线程情况下,为了保证数据一致性,在读写访问时都将锁住这个文件,读写线程可能都运行在相同的优先级,现在拥有文件锁的线程可能会周期性的将控制权转让给另一个与之竞争的线程。
需要注意的是,yield()方法对JVM来说是“提示”,而不是强制要求,也没有结束。JVM无法保证线程调度的确定性,这一点我们会在下面的例子展示出来,除此之外也不能确定这个提示是让更低级的线程获得控制权还是同级的线程获得控制权,尽管大多数情况下是同级线程获得控制权的。这个方法比较不稳定,一般不常用。它所表达的意思口语化一点就是:“我急获得了足够的CPU时间,想让其他线程有机会运行,我将在一段时间后运行剩余的代码”,这与sleep方法不一样,sleep的意思是:“在n毫秒的时间内我不想运行,就算没有其他线程想运行,也别让我运行”。
package com.hxw.Threads; /** * @author Rollen-Holt 线程的优先级 * */ class hello2 implements Runnable { synchronized publicvoid run() { for(int i=0;i<12;++i){ System.out.println(Thread.currentThread().getName()+"运行"+i); if(i==3){ Thread.currentThread().yield(); System.out.println(Thread.currentThread().getName()+"将自己的线程礼让出来了"); } } } publicstaticvoid main(String[] args) { Thread h1=new Thread(new hello2(),"A"); Thread h2=new Thread(new hello2(),"B"); h1.start(); h2.start(); } }
A运行0
B运行0
A运行1
B运行1
A运行2
B运行2
A运行3
A将自己的线程礼让出来了
A运行4
A运行5
A运行6
A运行7
A运行8
A运行9
A运行10
A运行11
A运行12
B运行3
B将自己的线程礼让出来了
B运行4
B运行5
B运行6
B运行7
B运行8
B运行9
B运行10
B运行11
B运行12
相信读者看完上面的结果想“呵呵!”了,A真不客气,都说将自己的线程礼让出来了还愣是把自己运行完了才把控制权交给B,这就是yiel()方法的“提示”转让。当然有些时候还会准确的让出控制权的。
6.线程的中断(打扰):
中断的原理:Java中断机制是一种协作机制,也就是说通过中断并不能直接终止另一个线程,而需要被中断的线程自己处理中断。这好比是家里的父母叮嘱在外的子女要注意身体,但子女是否注意身体,怎么注意身体则完全取决于自己。
Java中断模型也是这么简单,每个线程对象里都有一个boolean类型的标识(不一定就要是Thread类的字段,实际上也的确不是,这几个方法最终都是通过native方法来完成的),代表着是否有中断请求(该请求可以来自所有线程,包括被中断的线程本身)。例如,当线程t1想中断线程t2,只需要在线程t1中将线程t2对象的中断标识置为true,然后线程t2可以选择在合适的时候处理该中断请求,甚至可以不理会该请求,就像这个线程没有被中断一样。
关于线程的中断/打扰有三个重要方法,interrupt,isInterrupted,interrupted
interrupt: 中断(打扰)线程。
interrupted:静态方法,测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(即线程状态为非中断状态,而其实已经是终端状态,知识没有了这个终端标识了而已)。
isInterrupted: 测试线程是否已经中断。这个方法不是静态的,调用是需要对象引用,而且这个方法不会清空中断标志。
当另一个线程通过调用 Thread.interrupt()
中断一个线程时,会出现以下两种情况之一。一种情况正常的话会设置该线程的终端状态,但是如果那个线程在执行一个低级可中断阻塞方法,例如Thread.sleep()
、 Thread.join()
或 Object.wait()
,那么它将取消阻塞并抛出 InterruptedException
。
注意:j2se 1.2开始,stop,suspend,resume方法就已经不提倡使用了,因为他们容易造成死锁。
摘录自一本书:反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
这个中断的例子匆忙举出一个是不太准确的,后面会专门写一个文章来测试这个用法。
7. 守护线程和非守护线程
JVM中存在两种线程:用户线程和守护线程。
所谓的守护线程,是指用户程序在运行的时候后台提供的一种通用服务的线程,比如用于垃圾回收的
垃圾回收线程。这类线程并不是用户线程不可或缺的部分,只是用于提供服务的"服务线程"。
基于这个特点,当虚拟机中的用户线程全部退出运行时,守护线程没有服务的对象后,JVM也就退出了,反之还有任意一个用户线程在,JVM都不会退出。
我们如何开始一个自定义的守护进程呢?正如上述代码一样,答案很简单,就是在Thread.start()方法之前使用setDaemon(true)方法,通过此方法将Thread类中的boolean daemon=true;JVM就会将该线程归为守护线程
说完了守护线程如何产生和特点,下面简要的谈谈使用守护线程应该注意的地方。
1、thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个异常。你不能把正在运行
的常规线程设置为守护线程。
2、在守护线程中产生的线程也是守护线程。(这点读者可结合工具自己验证)
3、我们自己产生的守护线程应该避免访问一些类似于文件、数据库等固有资源,因为由于JVM没有用户
线程之后,守护线程会马上终止。
这一个不做例子,因为在eclipse查看不了jvm退出后其他用户线程的动作,所以查看起来比较复杂,需要用jvisualvm.exe 来看,这一点会再另外写一篇文章阐述。
8. 关于非常重要的wait和notify方法我在下一篇讲线程同步的时候再写
以上是线程的基本用法,下一篇是关于线程的同步,因为我写的比较详细,也参考了很多资料,希望能把即使比较偏的知识点也囊括进来,如果文章有什么问题和要改进的地方,请提出来大家讨论,我会一一回复,我非常想交一些技术上的朋友。
相关推荐
内容概要:本文详细介绍了基于MATLAB GUI界面和卷积神经网络(CNN)的模糊车牌识别系统。该系统旨在解决现实中车牌因模糊不清导致识别困难的问题。文中阐述了整个流程的关键步骤,包括图像的模糊还原、灰度化、阈值化、边缘检测、孔洞填充、形态学操作、滤波操作、车牌定位、字符分割以及最终的字符识别。通过使用维纳滤波或最小二乘法约束滤波进行模糊还原,再利用CNN的强大特征提取能力完成字符分类。此外,还特别强调了MATLAB GUI界面的设计,使得用户能直观便捷地操作整个系统。 适合人群:对图像处理和深度学习感兴趣的科研人员、高校学生及从事相关领域的工程师。 使用场景及目标:适用于交通管理、智能停车场等领域,用于提升车牌识别的准确性和效率,特别是在面对模糊车牌时的表现。 其他说明:文中提供了部分关键代码片段作为参考,并对实验结果进行了详细的分析,展示了系统在不同环境下的表现情况及其潜在的应用前景。
嵌入式八股文面试题库资料知识宝典-计算机专业试题.zip
嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_3.zip
内容概要:本文深入探讨了一款额定功率为4kW的开关磁阻电机,详细介绍了其性能参数如额定功率、转速、效率、输出转矩和脉动率等。同时,文章还展示了利用RMxprt、Maxwell 2D和3D模型对该电机进行仿真的方法和技术,通过外电路分析进一步研究其电气性能和动态响应特性。最后,文章提供了基于RMxprt模型的MATLAB仿真代码示例,帮助读者理解电机的工作原理及其性能特点。 适合人群:从事电机设计、工业自动化领域的工程师和技术人员,尤其是对开关磁阻电机感兴趣的科研工作者。 使用场景及目标:适用于希望深入了解开关磁阻电机特性和建模技术的研究人员,在新产品开发或现有产品改进时作为参考资料。 其他说明:文中提供的代码示例仅用于演示目的,实际操作时需根据所用软件的具体情况进行适当修改。
少儿编程scratch项目源代码文件案例素材-剑客冲刺.zip
少儿编程scratch项目源代码文件案例素材-几何冲刺 转瞬即逝.zip
内容概要:本文详细介绍了基于PID控制器的四象限直流电机速度驱动控制系统仿真模型及其永磁直流电机(PMDC)转速控制模型。首先阐述了PID控制器的工作原理,即通过对系统误差的比例、积分和微分运算来调整电机的驱动信号,从而实现转速的精确控制。接着讨论了如何利用PID控制器使有刷PMDC电机在四个象限中精确跟踪参考速度,并展示了仿真模型在应对快速负载扰动时的有效性和稳定性。最后,提供了Simulink仿真模型和详细的Word模型说明文档,帮助读者理解和调整PID控制器参数,以达到最佳控制效果。 适合人群:从事电力电子与电机控制领域的研究人员和技术人员,尤其是对四象限直流电机速度驱动控制系统感兴趣的读者。 使用场景及目标:适用于需要深入了解和掌握四象限直流电机速度驱动控制系统设计与实现的研究人员和技术人员。目标是在实际项目中能够运用PID控制器实现电机转速的精确控制,并提高系统的稳定性和抗干扰能力。 其他说明:文中引用了多篇相关领域的权威文献,确保了理论依据的可靠性和实用性。此外,提供的Simulink模型和Word文档有助于读者更好地理解和实践所介绍的内容。
嵌入式八股文面试题库资料知识宝典-2013年海康威视校园招聘嵌入式开发笔试题.zip
少儿编程scratch项目源代码文件案例素材-驾驶通关.zip
小区开放对周边道路通行能力影响的研究.pdf
内容概要:本文探讨了冷链物流车辆路径优化问题,特别是如何通过NSGA-2遗传算法和软硬时间窗策略来实现高效、环保和高客户满意度的路径规划。文中介绍了冷链物流的特点及其重要性,提出了软时间窗概念,允许一定的配送时间弹性,同时考虑碳排放成本,以达到绿色物流的目的。此外,还讨论了如何将客户满意度作为路径优化的重要评价标准之一。最后,通过一段简化的Python代码展示了遗传算法的应用。 适合人群:从事物流管理、冷链物流运营的专业人士,以及对遗传算法和路径优化感兴趣的科研人员和技术开发者。 使用场景及目标:适用于冷链物流企业,旨在优化配送路线,降低运营成本,减少碳排放,提升客户满意度。目标是帮助企业实现绿色、高效的物流配送系统。 其他说明:文中提供的代码仅为示意,实际应用需根据具体情况调整参数设置和模型构建。
少儿编程scratch项目源代码文件案例素材-恐怖矿井.zip
内容概要:本文详细介绍了基于STM32F030的无刷电机控制方案,重点在于高压FOC(磁场定向控制)技术和滑膜无感FOC的应用。该方案实现了过载、过欠压、堵转等多种保护机制,并提供了完整的源码、原理图和PCB设计。文中展示了关键代码片段,如滑膜观测器和电流环处理,以及保护机制的具体实现方法。此外,还提到了方案的移植要点和实际测试效果,确保系统的稳定性和高效性。 适合人群:嵌入式系统开发者、电机控制系统工程师、硬件工程师。 使用场景及目标:适用于需要高性能无刷电机控制的应用场景,如工业自动化设备、无人机、电动工具等。目标是提供一种成熟的、经过验证的无刷电机控制方案,帮助开发者快速实现并优化电机控制性能。 其他说明:提供的资料包括详细的原理图、PCB设计文件、源码及测试视频,方便开发者进行学习和应用。
基于有限体积法Godunov格式的管道泄漏检测模型研究.pdf
嵌入式八股文面试题库资料知识宝典-CC++笔试题-深圳有为(2019.2.28)1.zip
少儿编程scratch项目源代码文件案例素材-几何冲刺 V1.5.zip
Android系统开发_Linux内核配置_USB-HID设备模拟_通过root权限将Android设备转换为全功能USB键盘的项目实现_该项目需要内核支持configFS文件系统
C# WPF - LiveCharts Project
少儿编程scratch项目源代码文件案例素材-恐怖叉子 动画.zip
嵌入式八股文面试题库资料知识宝典-嵌⼊式⼯程师⾯试⾼频问题.zip