一、进程与线程的区别
(1)、进程是程序调度的基本单位,也是操作系统分配资源的单位,平常所说的运行的一个应用就是一个进程。线程是cpu资源调度的基本单位,一个进程能够包含多个线程,平常所说的一个应用里面的一个功能就相当于是一个线程。
(2)、进程之间不能共享资源,是相互独立的单位,拥有独立的内存资源;但是线程之间内存空间共享,也就是说资源之间可以共享,极大地提高了程序运行的效率。
(3)、进程之间相互切换开销较大,而线程之间相互切换开销较小。
使用多线程的好处是可以充分利用系统资源,将一个进程划分为多个线程,可以充分发挥多核cpu的优势(如果在一个设备上进行多个操作,在宏观上相当于是同时进行处理);同时用户等待程序响应得时间也会更快,因为线程之间的上下文切换更加快速。
二、java中创建线程的几种方式
(1)、继承Thread类,重写run()方法。一般写法是:
class test extends Thread{
@Override
public void run() {
//此处为你需要执行的代码
}
}
但是这种方式的缺点是线程类不能进行其他拓展,因为java是单继承,如果需要拓展其他类的功能,推荐使用下面几种方式。
(2)、实现Runnable接口,重写run()方法。一般写法是:
class Test implements Runnable{
@Override
public void run() {
//此处为你需要执行的代码
}
}
调用这个线程的方式是:
Thread thread = new Thread(new Test());
thread.run();
(3)、实现Callable接口,通过FutureTask包装器来创建线程。一般写法是:
class Test implements Callable<Integer>{
@Override
public Integer call() throws Exception{
//此处填写你需要执行的代码
//执行完毕可以返回你需要的值
return null;
}
}
创建并且调用该线程的写法是:
//使用Callable方式,需要FutureTask包装类的支持,用于接受运算结果
FutureTask<Integer> futureTask = new FutureTask<>(new Test());
new Thread(futureTask).start();
// 2.接收线程运算后的结果
Integer sum;
try {
//等所有线程执行完,获取值,因此FutureTask 可用于 闭锁
sum = futureTask.get();
System.out.println("-----------------------------");
System.out.println(sum);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
使用该方式创建线程的好处就是,线程执行完毕之后会返回一个值给主线程。
(4)、使用线程池创建线程。一般创建线程主题和第二种方式是一样的:
class Test implements Runnable{
@Override
public void run() {
//此处填写你需要执行的代码
}
}
接下来是创建线程并且执行该线程的方式是:
//创建一个固定最大线程数量的线程池,这里规定的最大数量是5
ExecutorService executorService = Executors.newFixedThreadPool(5);
//执行该线程
executorService.execute(new Test());
//线程池不再接受新的任务(相当于关闭线程池)
executorService.shutdown();
三、java内存模型
(1)、主内存与工作内存
jmm是一个抽象的概念,是为了保证java程序能够在不同的操作系统以及不同cpu型号等等条件下,java程序都能正常的运行,使其能够正常的获得程序中的指定的变量的一组规则。程序中的所有的变量都存在在主内存中(包括实例变量,静态变量以及数组对象的元素,不包括局部变量,因为它们线程私有)。在虚拟机中运行的是一个个的线程,每个线程也有自己的工作空间,用于存放该线程所用到的实例变量等等,这些变量是从主内存空间拷贝的副本变量,线程操作变量直接从自己的工作空间进行操作,之后再同步到主内存中;不同的线程之间不能直接访问对方的变量,只能通过主内存进行传递。
(2)、内存间交互
(3)、指令重排序
(4)、同步机制
(5)、原子性、可见性与有序性