1.进程和线程的区别:
进程 :进程是程序的一次执行过程,是一个正在执行的程序,一个程序可以同时执行多个任务(线程)。进程独占内存空间,保存各自的运行状态,互相之间不会干扰。是并发执行程序在执行过程资源分配和管理的基本单位(资源分配的最小单位)。每个进程都有自己的独立的地址空间,每启动一个进程,系统就会分配地址空间。在任务管理器中可以看到很多的进程。
线程:程序执行的最小单位,一个进程可以有多个线程,多个线程之间共享进程的地址空间及进程级别的其它资源,但各个线程拥有自己的栈空间。
进程与线程的使用场景区分: 如果在程序中需要频繁的创建和销毁就使用线程。因为进程创建和销毁的开销很大。如果程序需要更强的稳定性和安全性时,可以选择进程,追求速度就选择线程。
提高系统并发能力的方法:
垂直扩展
- 提升单机的处理能力
- 1)增强单机的硬件性能:增加CPU的核数、内存升级、磁盘扩容
- 2)提升系统的架构能力:使用Cache来提高效率
- · 水平扩展
- 集群、分布式都是水平的扩展方案
- 集群:多个人做同一事(同时多顾几个厨师同时炒菜)
- 分布式:一个复杂的事情,拆分成几个简单的步骤,分别找不同的人去完成(1.洗菜 2.切菜 3.炒菜)
- 1)站点层扩容:通过Nginx反向代理,实现高并发的系统,将服务部署在多个服务器上
- 2)服务层扩容:通过RPC框架实现远程调用:Dubbo,Spring Clodud,将业务逻辑分拆成不同的RPC Client,
- Clident完成各自的不同的业务,如果并发量比较大,新增加RPC Client
- 3)数据层扩容:一台数据库拆分成多态,分库分表,主从复制,读写分离
2.并发和并行的区别:
并发: 多个线程操作一个资源,不是同时执行,很短的时间内可以交替执行多条程序命令,所以看起来像同时执行,单核cpu就可以做到。
并行: 在同一个时间多个任务同时进行,需要多核cpu才可以做到,每一个线程可以使用单独的cpu资源来运行。
3.创建线程的方法:
(1) 继承Thread类,重新run()方法,新线程类创建方法
class MyThread extends Thread{
@Override
public void run() {
System.out.println("hello world");
}
}
public class Test {
public static void main(String[] args) {
MyThread thread = new MyThread();//实例化线程对象
thread.start();//启动对象
}
}
(2)实现Runnable接口,实现接口中的run()方法,Thread类创建线程。
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("implemnts Runnable to get a thread");
}
}
public class Test {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
}
(3) 匿名线程,通过匿名内部类实现:
public class Test {
public static void main(String[] args) {
new Thread("watchTV"){
@Override
public void run() {
System.out.println("watch TV");
}
}.start();
}
}
(4)实现Callable接口,重写call()方法:
/**
* 通过Callable和FutureTask创建线程
* a.创建Callable接口的实现类,同时实现call方法
* b.创建Callable实现类的对象,使用FutureTask包装当前Callable对象,
* FutureTask对象封装Callable对象中call方法的返回
* c.使用FutureTask对象作为Thread的参数创建并且启动线程
* d.调用FutureTask对象的get()来获取子线程执行的结果
*/
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 10000; i++) {
sum += i;
}
return sum;
}
}
public class Test {
public static void main(String[] args) {
Callable<Integer> callable = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(callable);
Thread thread = new Thread(task);
thread.start();
try {
Integer result = task.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
Callable接口和Runnable接口的区别:
1. Callable接口实现的是call方法,Runnable接口实现的是run方法
2. Callable执行后有返回值,Runnable无返回值
3. call()方法会抛出异常,run方法不能抛出异常
4. Callable使用了Futuretask,可以拿到Callable异步计算的结果,通过Futuretask中的get方法可以获取当前的结果,并且可以通过get方法来检查当前任务是否已经完成。
4.守护线程:
JVM总没有一个非守护线程,JVM进程会退出
守护线程能够自动结束生命周期,非守护则不具备这一特点
非守护线程测试:
public class Test {
public static void main(String[] args) {
Thread thread = new Thread("sleep") {
@Override
public void run() {
try {
while (true) {
TimeUnit.SECONDS.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread fininshed");
}
}
结果:
因为为非守护线程,所以子线程并没有自动结束。当把子线程改为守护线程后:
public class Test {
public static void main(String[] args) {
Thread thread = new Thread("sleep") {
@Override
public void run() {
try {
while (true) {
TimeUnit.SECONDS.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.setDaemon(true);
thread.start();
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread fininshed");
}
}
结果:
5.线程的生命周期:
生命周期:
- NEW(新建状态):new Thread()创建线程对象
- RUNNABLE(就绪状态):线程对象此时调用start方法,此时在JVM进程中创建了一个线程,线程并不是一经创建就直接得到执行,需要等到操作系统的其他资源,比如:处理器
- BLOCKED(阻塞状态):等到一个监视器锁进入到同步代码块或者同步方法中,代码块/方法某一个时刻只允许一个线程去执行,其他线程只能等待,这种情况下等待的线程会从 RUNNABLE状态转换到BLOCKED状态 Objcet.wait()
- WAITING(等待状态):调用Object.wait()/join()/LockSupport.park()等方法,此时线程从RUNNABLE转换到WAITING状态
- TIMED_WAITING(睡眠状态):调用带超时参数的THread.sleep(long millis)/Object.wait(long timeout)/join(long milles)/LockSupport.parkNanos()/ LockSupport.parkUntil等方法都会使得当前线程进入到TIMED_WAITING状态
- TERMINATED(终止状态): 是线程的最终状态
1.线程正常运行结束 2.线程运行出错 3.JVM crash
线程六状态间的转换图:
6.线程中的常用方法:
- start()
用来启动一个线程,并且将该线程添加到一个线程组当中。此时线程处于Runnable就绪状态. - sleep()
Thread.sleep(),使得当前线程进入指定毫秒级的休眠,暂停执行当前线程,不会放弃monitor锁的使用权。jdk1.5之后引入了枚举类型TimeUnit,对sleep方法进行了封装,可以给定休眠的时间单位。sleep()是一种可中断方法。
public class Test2 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
- yield()
属于启发式的方法,线程A.yield(),会提醒调度器线程A愿意放弃本次的cpu资源,如果cpu资源不紧张,cpu可能会忽略这种提醒。yield()是一种不可中断方法。 - join()
在thread B中调用thread A.join()时,thread B进入到等待状态,直到thread A结束自己的生命周期或者达到join方法的超时时间。
public class Test2 {
public static void main(String[] args) {
Thread A = new Thread("A") {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("thread: " + Thread.currentThread().getName() + "," + i);
}
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread B = new Thread("B") {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("thread: " + Thread.currentThread().getName() + "," + i);
}
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
try {
A.start();
A.join();
B.start();
B.join();
for (int i = 0; i < 5; i++) {
System.out.println("thread: " + Thread.currentThread().getName() + "," + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
5. 实现线程中断的方法:
(1)interrupt() 将java线程当中的终端位置为true,thread A : sleep()/join()/wait throw InterruptedException 可中断方法,以上方法都会使得当前进入阻塞状态,另外一个线程调用被阻塞线程的interrupt方法会打断当前的这种阻塞状态,抛出一个InterruptedException的异常,这样的方法称之为可中断方法。 并不是结束当前被阻塞线程的生命周期,只是打断了当前线程的阻塞状态。
public class Test2 {
public static void main(String[] args) {
Thread A = new Thread("A") {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
A.start();
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
A.interrupt();
}
}
(2)isInterrupted() 判断中断状态位是否为true
public class Test2 {
public static void main(String[] args) {
Thread A = new Thread("A") {
@Override
public void run() {
while (true);
}
};
A.start();
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(A.isInterrupted());
A.interrupt();
System.out.println(A.isInterrupted());
}
}
(3)interrupted() 判断中断状态位是否为true,与isInterrupted的区别为调用之后会擦除掉线程的interrupt标识
public class Test2 {
public static void main(String[] args) {
Thread A = new Thread("A") {
@Override
public void run() {
while (true){
System.out.println(Thread.interrupted());
}
}
};
A.setDaemon(true);
A.start();
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
A.interrupt();
}
}
- wait/notify/notifyALL
wait调用该synchornized同步代码块或方法当中,使得当前线程进入阻塞,notify/notifyAll唤醒当前的阻塞。均为Object类中的方法。