JavaSE 学习笔记06丨并发
Chapter 12. 并发
12.1 并发与并行
- 并发:指两个或多个事件在同一个时间段内发生。
- 并行:指两个或多个事件在同一时刻发生(同时发生)。
在操作系统中,并发指的是在一段时间内宏观上有多个程序同时运行。
在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,而这个分时交替运行的时间是非常短的。
注意:单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
在多个 CPU 系统中,则这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序便可以同时执行。多核处理器,核 越多,并行处理的程序越多,能大大的提高电脑运行的效率。
12.2 线程与进程
-
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程(即一个程序运行后至少有一个进程);进程也是程序的一次执行过程,是系统运行程序的基本单位。
-
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行。一个进程中是可以有多个线程的(即一个进程中至少有一个线程),这个应用程序也可以称之为多线程程序。
同一进程中的线程共享其进程中的内存(堆内存、方法区内存,但栈内存不共享,因为一个线程一个栈)和资源。
线程调度:
-
分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
-
抢占式调度
优先让优先级高的线程使用 CPU ,如果线程的优先级相同,那么会随机选择一个线程(体现出线程随机性),Java使用的是抢占式调度。大部分操作系统都支持多进程并发运行,几乎都支持同时运行多个程序。但实际上,CPU使用抢占式调度模式在多个线程间进行着高速的切换,对于CPU一个核而言,某个时刻只能指向一个线程,而CPU的在多个线程间切换速度十分块。
12.3 创建线程
12.3.1 继承Thread类以创建线程
Java使用 java.lang.Thread
类代表线程,所有线程对象都必须是Thread
类或其子类的实例。每个线程实际上就是顺序执行一段代码。Java使用线程执行体来代表这段程序流,如下创建并启动多线程:
一、定义子类,覆写方法:
定义Thread
类的子类,并覆写该类的run()
方法(它代表线程需完成的任务,故称之为线程执行体)。
对于Thread
类,其构造方法有:
public Thread()
public Thread(String name)
:以指定名字name
创建新的线程对象public Thread(Runnable target)
:带有指定目标target
创建新的线程对象public Thread(Runnable target, String name)
:创建一个带有指定目标target
且指定名字为name
的线程对象。
自定义线程类举例如下:
public class MyThread extends Thread{
public MyThread(String name){ //构造方法,指定线程名称
super(name); //需要调用父类Thread的String参数的构造方法,以指定线程名称
}
@Override
public void run(){
for(int i = 0; i < 10; i++){
System.out.println(getName() + ":" + i);
}
} //覆写run方法,完成该线程执行的逻辑
}
二、创建实例,启动线程:
创建线程,即创建了线程对象。接着调用线程对象的start
方法以启动该线程。
对于Thread
类,其常用方法有:
public String getName()
:获取当前线程名称。public void start()
:使该线程开始执行,JVM调用此线程的run
方法。public void run()
:此线程要执行的任务在此处定义。public static void sleep(long millis)
:使当前正在执行的线程以执行毫秒数millis
暂停(暂时停止执行)。public static Thread currentThread()
:返回对当前正在执行的线程对象的引用。
测试类举例如下:
public class JustTest {
public static void main(String[] args) {
System.out.println("这里是main线程");
MyThread mt = new MyThread("另一条新线程"); //创建自定义线程对象
mt.start(); //开启新线程
for(int i = 0; i < 10; i++){
System.out.println("main线程:" + i);
}
}
}
Java中,每个程序运行至少启动2个线程(
main
线程,及垃圾收集线程)。
12.3.2 实现Runnable接口以创建线程
对于java.lang.Runnable
,我们只需覆写其run
方法即可。
一、定义实现类,覆写方法:
定义Runnable
接口的实现类,并覆写该接口的run
方法(该方法体同样为该线程的线程执行体)
举例如下:
public class MyRunnable implements Runnable {
@Override
public void run(){
for(int i = 0; i < 20; i++)
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
二、创建实例,启动线程
创建Runnable
实现类的实例,并以此实例作为Thread
的target
来创建Thread
对象(对应于Thread
的构造方法Thread(Runnable target)
),注意,该Thread
对象才是真正的线程对象。接着,调用线程对象的start()
来启动线程。
public class JustTest {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr, "另一线程");
t.start();
for(int i = 0; i < 20; i++){
System.out.println("主线程" + i);
}
}
}
Runnable
对象仅作为Thread
对象的target
,Runnable
实现类包含的run
方法仅作为线程执行体,而实际的线程对象仍是Thread
实例,只是该Thread
线程负责执行其target
的run()
方法。
相比于直接继承Thread
类,实现Runnable
接口所具有的优势:
- 多个相同的程序代码的线程能够共享同一资源;
- 避免Java中的单继承的局限性;
- 增加程序的健壮性,实现解耦操作,代码能被多个线程共享,代码与线程独立;
- 线程池只能放入实现
Runnable
或Callable
类线程,不能放入直接继承Thread
的类;
12.3.3 匿名内部类方式 创建线程
使用匿名内部类的方式实现Runnable
接口,覆写Runnable
接口中的run
方法,举例如下:
public class JustTest {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
for(int i = 0; i < 20; i++)
System.out.println("另一线程:"+i);
}
};
new Thread(r).start();
for(int i = 0; i < 20; i++){
System.out.println("main线程:" + i);
}
}
}
多线程这一模块的知识,暂时先学到这,咕一咕。🤪