目录
一、进程
1、概述
进程:程序是静止的,程序实体的运行过程就是进程,是系统进行资源分配的基本单位
进程的特征:并发性、异步性、动态性、独立性、结构性
线程:线程是属于进程的,是一个基本的 CPU 执行单元,是程序执行流的最小单元。线程是进程中的一个实体,是系统独立调度的基本单位,线程本身不拥有系统资源,只拥有一点在运行中必不可少的资源,与同属一个进程的其他线程共享进程所拥有的全部资源
关系:一个进程可以包含多个线程,这就是多线程,比如看视频是进程,图画、声音、广告等就是多个线程
线程的作用:使多道程序更好的并发执行,提高资源利用率和系统吞吐量,增强操作系统的并发性能
并发并行:
- 并行:在同一时刻,有多个指令在多个 CPU 上同时执行
- 并发:在同一时刻,有多个指令在单个 CPU 上交替执行
同步异步:
- 需要等待结果返回,才能继续运行就是同步
- 不需要等待结果返回,就能继续运行就是异步
2、进程和线程
-
进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
-
进程拥有共享的资源,如内存空间等,供其内部的线程共享
-
进程间通信较为复杂
同一台计算机的进程通信称为 IPC(Inter-process communication)
- 信号量:信号量是一个计数器,用于多进程对共享数据的访问,解决同步相关的问题并避免竞争条件
- 共享存储:多个进程可以访问同一块内存空间,需要使用信号量用来同步对共享存储的访问
- 管道通信:管道是用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件 pipe 文件,该文件同一时间只允许一个进程访问,所以只支持半双工通信
- 匿名管道(Pipes):用于具有亲缘关系的父子进程间或者兄弟进程之间的通信
- 命名管道(Names Pipes):以磁盘文件的方式存在,可以实现本机任意两个进程通信,遵循 FIFO
- 消息队列:内核中存储消息的链表,由消息队列标识符标识,能在不同进程之间提供全双工通信,对比管道:
- 匿名管道存在于内存中的文件;命名管道存在于实际的磁盘介质或者文件系统;消息队列存放在内核中,只有在内核重启(操作系统重启)或者显示地删除一个消息队列时,该消息队列才被真正删除
- 读进程可以根据消息类型有选择地接收消息,而不像 FIFO 那样只能默认地接收
不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
- 套接字:与其它通信机制不同的是,可用于不同机器间的互相通信
-
线程通信相对简单,因为线程之间共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
Java 中的通信机制:volatile、等待/通知机制、join 方式、InheritableThreadLocal、MappedByteBuffer
-
线程更轻量,线程上下文切换成本一般上要比进程上下文切换低
3、并发与并行
(1)并发——微观串行,宏观并行
在单核 cpu 下,线程实际还是串行执行的。
(2)并行——真正的同时运行
多核 cpu下,每个核(core) 都可以调度运行线程,这时候线程可以是并行的,不同的线程同时使用不同的cpu在执行
(3)对比
-
并发(concurrent)是同一时间应对(dealing with)多件事情的能力
-
并行(parallel)是同一时间动手做(doing)多件事情的能力
4、同步与异步
以调用方的角度讲,如果
- 需要等待结果返回才能继续运行的话就是同步
- 不需要等待就是异步
注意:同步在多线程中还有另外一层意思,即让多个线程步调一致
5、新线程的创建
5.1 使用Thread
// 创建线程对象
Thread t = new Thread() {
public void run() {
// 要执行的任务
}
};
// 启动线程
t.start();
5.2 使用Runnable配合Thread
把线程和任务(要执行的代码)分开
- Thread代表线程
- Runnable可运行的任务(线程要执行的代码)
Runnable runnable = new Runnable() {
public void run(){
// 要执行的任务
}
};
// 创建线程对象
Thread t = new Thread( runnable );
// 启动线程
t.start();
使用lambda表达式精简代码
// 创建任务对象
Runnable task2 = () -> log.debug("hello");
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();
5.3 FutureTask配合Thread
FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
log.debug("hello");
return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
log.debug("结果是:{}", result);
输出:
19:22:27 [t3] c.ThreadStarter - hello
19:22:27 [main] c.ThreadStarter - 结果是:100
6、自定义线程类的创建
6.1 继承Thread类
(1)创建一个继承于 Thread 类的子类
(2)重写 Thread 类的 run() 方法,将此线程要执行的代码块写在 run() 方法中
(3)创建该子类的对象
(4)调用该对象的 start() 方法
//继承Thread类来创建线程
public class ThreadTest {
public static void main(String[] args) {
//设置线程名字
Thread.currentThread().setName("main thread");
MyThread myThread = new MyThread();
myThread.setName("子线程:");
//开启线程
myThread.start();
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
class MyThread extends Thread{
//重写run()方法
public void run(){
for(int i = 0;i < 10; i++){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
6.2 实现Runnable接口
(1)创建一个实现了Runnable接口的类
(2)实现Runnable中的抽象方法:run()
(3)创建实现类的对象
(4)将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
(5)通过Thread类的对象调用start()
/**
* 比较创建线程的两种方式:
* 开发中优先选择实现Runnable接口的方式
* 原因:1.实现的方式没有类的单继承性的局限性
* 2.实现的方式更适合来处理多个线程有共享数据的情况
*
* 联系:都实现了Runnable接口
* 相同点:两种方式都需要重写run()方法,将线程要执行的逻辑声明在run()中
*
*/
public class ThreadTest1 {
public static void main(String[] args) {
// * 3.创建实现类的对象
MyThread myThread = new MyThread();
// * 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread thread1 = new Thread(myThread);
// * 5.通过Thread类的对象调用start() ①启动线程 ②调用当前线程的run()
thread1.start();
Thread thread2 = new Thread(myThread);
thread2.start();
}
}
// * 1.创建一个实现了Runnable接口的类
class MyThread implements Runnable{
// * 2.实现类去实现Runnable中的抽象方法:run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
6.3 实现Callable接口
(1)创建一个实现Callable的实现类
(2)实现call方法,将此线程需要执行的代码声明在call()方法中
(3)创建Callable接口实现类的对象
(4)将Callable接口实现类的对象作为参数传递到Future Task构造器中,创建FutureTask的对象
(5)将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
(6)获取Callable中call方法的返回值
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 创建线程的方式三:实现Callable接口。 JDK5.0新增
*
* 如何理解实现Callable接口的方式创建多线程的方式比是实现Runnable接口创建多线程方式强大
* 1.call()可以有返回值
* 2.call()可以抛出异常
*/
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法
Thread thread1 = new Thread(futureTask);
thread1.start();
try {
//6.获取Callable中call方法中的返回值
//get()返回值即为FutureTask构造器参数Callable实现重写的call()返回值。
Object sum = futureTask.get();
System.out.println(" 总和为:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if(i % 2 == 0){
System.out.println(i);
sum += i;
}
}
return sum;
}
}
6.4 线程池的方式
(1)提供指定线程数量的线程池
(2)执行指定线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 创建线程的方式四:使用线程池
*
* 好处:1.提高响应速度(减少创建新线程的时间)
* 2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
* 3.便于线程管理
*
* corePoolSize:核心池的大小
* maximumPoolSize:最大线程数
* keepAliveTime:线程没有任务时最多保持多长时间后会终止
*/
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) executorService;
//设置线程池的属性
service1.setCorePoolSize(10);
//2.执行指定线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象
executorService.execute(new NumberThread());//适合使用于Runnable
//executorService.submit();//适合使用于Callable
executorService.execute(new NumberThread1());
//关闭线程池
executorService.shutdown();
}
}
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" +i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" +i);
}
}
}
}
标签:JUC,Runnable,Thread,创建,Callable,线程,进程
From: https://www.cnblogs.com/DarkSki/p/16985330.html