JUC系列——基础知识 day1-1
JUC基础知识
进程
程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。
进程就是用来加载指令、管理内存、管理IO的,当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程
线程
一个进程之内可以分为一到多个线程。
一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行
Java中,线程作为最小调度单位,进程作为资源分配的最小单位。在 windows 中进程是不活动的,只是作为线程的容器
进程和线程区别
- 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
- 进程拥有共享的资源,如内存空间等,供基内部的线程共享
- 进程间通信较为复杂
- 同一台计算机的进程通信称为IPC (Inter-process communication)
- 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如HTTP
- 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
- 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低
并行与并发
单核cpu下,线程实际还是串行执行的。
操作系统中有一个组件叫做任务调度器,将cpu的时间片(windows 下时间片最小约为15毫秒)分给不同的线程使用,只是由于cpu在线程间(时间片很短)的切换非常快,人类感觉是同时运行的。
总结为一句话就是:微观串行,宏观并行
线程轮流使用CPU的做法就是并发
在多核cpu下每个核都可以调度运行线程,这时候线程可以是并行的
- 并发(concurrent):同一时间应对多件事情的能力
- 并行(parallel):同一时间动手做多件事情的能力
同步
需要等待结果返回再去执行下一个“事件”就是同步
同步在多线程的可以指的是多个线程步调一致
使用场景
遇到即时性一定要有返回结果的业务时使用
异步
无需等待返回结果,直接进行下一个“事件”就是异步
多线程能够让方法执行变为异步的
使用情景
例如遇到一个业务会占用很长的时间时我们可以使用异步避免主线程被堵塞
QuickStart(new Thread方式创建新线程)
匿名内部类方式
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
//创建线程
final Thread thread = new Thread(){
@Override
public void run() {
System.out.println("我是新线程");
System.out.println(Thread.currentThread().getName());
}
};
System.out.println("我是主线程");
//启动线程
thread.start();
}
lambda简化
public static void main(String[] args) {
System.out.println("我是主线程");
System.out.println(Thread.currentThread().getName());
new Thread(()->{
System.out.println("我是新线程");
System.out.println(Thread.currentThread().getName());
}).start();
}
Thread类(仅分析功能)
优先级常量
- MAX_PRIORITY:线程可以拥有的最大优先级
- MIN_PRIORITY:线程可以拥有的最低优先级
- NORM_PRIORITY:分配给线程的默认优先级
方法(常用)
- checkAccess():确定当前运行的线程是否有权限 修改这个线程
- currentThread():返回对当前正在执行的线程对象的引用
- getId() :返回此线程的标识符。
- getName() :返回此线程的名称。
- getPriority() :返回此线程的优先级。
- getStackTrace() :返回表示堆栈转储的堆栈跟踪元素数组 这个线程的。
- getState() :返回此线程的状态。
- getThreadGroup() :返回该线程所属的线程组。
- interrupt() :中断这个线程。
- interrupted() :测试当前线程是否被中断。
- isAlive() :测试此线程是否存在。
- isDaemon() :测试此线程是否为守护线程。
- isInterrupted() :测试此线程是否已被中断。
- join() join(long millis) join(long millis, int nanos) :等待这个线程死掉。参数表示等待时间
- run() : 如果此线程是使用单独的 Runnable运行对象,然后 Runnable对象的 run方法被调用; 否则,此方法不执行任何操作并返回
- setDaemon(boolean on) :将此线程标记为 守护 线程 或用户线程。
- setName(String name) :将此线程的名称更改为等于参数 name.
- setPriority(int newPriority) : 更改此线程的优先级。
- sleep(long millis) :使当前执行的线程休眠(暂时停止 执行)为指定的毫秒数,受制于 系统计时器和调度程序的精度和准确性。
- start() :使该线程开始执行; Java虚拟机 调用 run这个线程的方法。
- yield() :向调度程序提示当前线程愿意让步 它当前使用的处理器。
使用Runnable创建线程
创建TmpThread类实现Runnable接口,重写run方法
public class TmpThread implements Runnable {
@Override
public void run() {
System.out.println("我是新线程");
}
}
在new Thread
的时候传入TmpThread的实例,调用start方法开启
public static void main(String[] args) {
System.out.println("我是主线程");
System.out.println(Thread.currentThread().getName());
final TmpThread tmpThread = new TmpThread();
final Thread thread = new Thread(tmpThread);
System.out.println(thread.getName());
thread.start();
}
lambda简化
由于Runnable是函数式接口所以我们可以直接这样写
public static void main(String[] args) {
System.out.println("我是主线程");
System.out.println(Thread.currentThread().getName());
Runnable r = ()->{
System.out.println("我是新线程");
};
final Thread thread = new Thread(r);
System.out.println(thread.getName());
thread.start();
}
FutureTask来创建线程(可允许返回值)
FutureTask能够接收Callable类型的参数,用来处理有返回结果的情况
public static void main(String[] args) throws ExecutionException, InterruptedException {
final FutureTask<String> stringFutureTask = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
return "使用callable创建线程";
}
});
final Thread thread = new Thread(stringFutureTask);
thread.start();
//阻塞,直到结果返回
final String s = stringFutureTask.get();
System.out.println(s);
}
lambda简化
public static void main(String[] args) throws ExecutionException, InterruptedException {
final FutureTask<String> stringFutureTask = new FutureTask<String>(()->{
return "使用callable创建线程";
});
final Thread thread = new Thread(stringFutureTask);
thread.start();
//阻塞,直到结果返回
final String s = stringFutureTask.get();
System.out.println(s);
}