1.进程
进程指的是运行中的程序,一个运行中的app就是一个进程。
2.线程
线程由进程创建,是进程的一个实体。
一个进程可以拥有多个线程。例如迅雷,下载多个文件。
这里所说的线程指程序执行过程中的一个线程实体。JVM 允许一个应用并发执行多个线程。 Hotspot JVM 中的 Java 线程与原生操作系统线程有直接的映射关系。当线程本地存储、缓 冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作系统原生线程。 Java 线程结束,原生线程随之被回收。操作系统负责调度所有线程,并把它们分配到任何可 用的 CPU 上。当原生线程初始化完毕,就会调用 Java 线程的 run() 方法。当线程结束时,会释放原生线程和 Java 线程的所有资源。
Hotspot是JVM的实现方式 JVM算是Hotspot的规范。
Hotspot JVM 后台运行的系统线程主要有下面几个:
虚拟机线程 (VM thread) 这个线程等待 JVM 到达安全点操作出现。这些操作必须要在独立的线程里执行,因为当 堆修改无法进行时,线程都需要 JVM 位于安全点。这些操作的类型有:stop-theworld 垃圾回收、线程栈 dump、线程暂停、线程偏向锁(biased locking)解除。
周期性任务线程 这线程负责定时器事件(也就是中断),用来调度周期性操作的执行。
GC 线程 这些线程支持 JVM 中不同的垃圾回收活动。
编译器线程 这些线程在运行时将字节码动态编译成本地平台相关的机器码。 信号分发线程 这个线程接收发送到 JVM 的信号并调用适当的 JVM 方法处理。
单线程
多线程 一个程序可以开启多个线程
并发:同一时刻,多个任务交替进行,造成一种“貌似同时”的错觉。例如,单核cpu实现的多任务就是并发。
并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。
1.创建线程(继承Thread)
单线程(继承Thread)demo:
创建线程与线程的一些注意事项:
对于进程的运行情况,可以在Terminal中,使用jconsole监视进程的情况。
package Thread_;
/**
* @program:多线程和IO
* @descripton:
* @author:ZhengCheng
* @create:2021/9/14-20:40
**/
public class Thread01 {
public static void main(String[] args) {
// new a().run(10);
a d= new a();//提问?为什么调用的是.start,会用run方法
d.start();
//当main线程启动子线程Thread0时,主线程是会继续执行的
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i+Thread.currentThread().getName());
}
}}
//当一个类继承了Thread类,就可以当做一个线程使用。
//需要重写run , run是Thread继承的Runnable接口的方法
class a extends Thread{@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("miao "+i+this);
//miao 9Thread[Thread-0,5,main]
//休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}}
}
// public void run(int times){
// for (int i = 0; i < times; i++) {
// System.out.println("miao"+i);
// //休眠
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// }
}
在上述的demo中,可以看到,使用start可以直接调用override之后的run方法,具体原因????
其次,如果我们需要的是run方法,那为什么不在main方法中直接使用new nameThread().run呢?
注意:如果直接使用new nameThread().run,那么我们可以看成,此时是main线程调用了run方法,如果此时输出该线程的值,得到的结果是main。而并没有开启子线程。只有使用start才会开启子线程。
源码解析:
public synchronized void start(){
start0();
}
//start0() 是本地方法,由jvm调用,底层由c或者c++实现
//真正实现多线程的是start0 而非run//native方法是本地方法
//当start调用了start0时,其实该线程并不会立马执行,只是将线程变成了可运行状态。
//具体什么时候执行,取决于CPU,又CPU同一调度。
private native void start0(){
}关于native关键字:Java当中native方法的详解_先苦后甜似淡非定的博客-优快云博客_native方法
目前的大致理解:native 关键字是负责告知jvm,该方法由c或者cpp实现,需要从操作系统中找到我们所需要的方法。
所以native关键字的函数都是操作系统实现的,
java只能调用。
java是跨平台的语言,既然是跨了平台,所付出的代价就是牺牲一些对底层的控制,而java要实现对底层的控制,就需要一些其他语言的帮助,这个就是native的作用了
Java不是完美的,Java的不足除了体现在运行速度上要比传统的C++慢许多之外,Java无法直接访问到操作系统底层(如系统硬件等),为此Java使用native方法来扩展Java程序的功能。
可以将native方法比作Java程序同C程序的接口,其实现步骤:
1、在Java中声明native()方法,然后编译;
2、用javah产生一个.h文件;
3、写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);
4、将第三步的.cpp文件编译成动态链接库文件;
5、在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。
JAVA本地方法适用的情况
1.为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问
2.为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的
3.为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。 -- 转载
线程的实现(实现Runnable接口)
当一个类继承了别的类时,无法再继承Thread,那么其实现Runnable接口,同样可以开启线程。
package Thread_;
/**
* @program:多线程和IO
* @descripton:
* @author:ZhengCheng
* @create:2021/9/14-22:30
**/
public class Thread02 {
public static void main(String[] args) {
B a = new B();
new Thread(a).start();
}
}
class B extends Thread01 implements Runnable{
@Override
public void run() {
System.out.println("Runnable实现");
}
}
这里的底层使用的设计模式[代理模式]????
分析实现:(模拟代理模式)
package Thread_;
/**
* @program:多线程和IO
* @descripton:
* @author:ZhengCheng
* @create:2021/9/14-22:30
**/
public class Thread02 {
public static void main(String[] args) {
new ThreadProxy (new Cat).start();
}
}
class Animal{
}
class Cat extends Animal implements Runnable{
@Override
public void run(){
System.out.println("通过代理模式实现成功");
}
}
//线程代理类
class ThreadProxy implements Runnable{
private Runnable target = null ;//属性,类型是Runnable
@Override
public void run() {
if (target != null){
target.run();//这个run是Runnable中的run
/*
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
在Thread中下列描述,说明run的是target
What will be run.
private Runnable target;
*/
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
//我们调用的是start
public void start() {
start0();
}
//模拟底层,最终让我们实现run
private void start0() {
run();
}
}
具体的设计模式在学习更多之后可以查看上文。
好处:实现Runnable接口方式更加适合多个线程共享一个资源。因为可以传进同一个资源对象,从而避免单继承的限制。
其实在上述两种创建线程中,本质上都是创建Thread对象,只不过一种是直接重写Run方法,另一种是通过实例化Runnable对象,将对象传递给Thread的target,由target.run最终调用到对象的Run方法。
小问题:
1.一共有几类创建线程的方式?
2.哪种方法更好?(3点)多多思考
1.代码架构解耦的角度
2.新建线程和关闭线程的损耗
3.java不支持双继承
3.如果同时使用两种方法?会出现什么样的情况?
其他可以创建线程的方式(不算真正的创建,核心都是继承Thread或者Runnable)

被折叠的 条评论
为什么被折叠?



