进程和线程
- 简单来说,一个程序就是一个进程,一个程序的多项任务就是多线程。进程就是一个程序执行时的实例,一个程序运行起来就会创建一个进程,然后系统给它分配资源,把它放到进程就绪队列里,等CPU调度到它,就给它分配调度时间,真正运行起来。一个线程就是一条执行路径。
- 进程是系统分配资源的基本单位,线程是系统调度的基本单位;一个进程可以有多条线程,一条线程只能属于一个进程,但每个进程里面肯定会有一条主线程;一个进程里面的资源可以共享,每个线程都有各自的虚拟机栈,本地方法栈,程序计数器来保存自己的信息(上下文)。
- 举个例子:曾经看到过别的大佬举的一个例子:把上课看成一个进程,老师就是一个CPU,正在听课的学生就是线程,这时有一个学生A突然听不懂了,就举手问老师,然后老师就给他去讲解,正在给A讲解的时候突然学生B又听不懂了,B又举手,然后老师又给B去讲解,给B讲完后又接着给A将。
- 并发:并发是“假同时”,单核CPU在同一时间只能运行一个任务,CPU采用时间片轮转调度的方式来运行任务,也就是一会执行A任务,一会执行B任务·····,由于切换的速度非常快,给我们的感觉就好像几个任务同时在运行。
- 并行:“真同时”,在多核CPU情况下,在同一时间每个CPU都在运行一个任务,真正做到了在同一时间同时有多个任务在运行。
- 举个例子:现在有A,B,C·····10个任务,并发:我一会做会A任务,一会做会B任务,一会做会C任务,这几个任务来回切换着做。 并行:我又找来了我的一个小伙伴,我俩同时做任务,在同一时间真的有多个任务在运行。
创建线程的几种方式
- 继承Thread类,重写run()方法,是将run()方法写在子类中,重写后就直接调用重写的run()方法,不再进行Runnable是否为空的判断,但是因为Java中是单继承,继承了Thread类,就不能再继承别的类了,所以会给继承方面带来缺陷。
public class Test {
public static void main(String[] args) {
MyThread thread=new MyThread();
thread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
- 实现Runnable接口,覆写run()方法,然后将Runnable丢给Thread,
让Thread包裹一下。并没有重写父类方法,所以会首先判断Runnable是否为空,如果不为空,就去执行自己写的run()。
public class Test {
public static void main(String[] args) {
MyRunnable r=new MyRunnable();
new Thread(r).start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
- 实现Callable接口,重写call()方法,实现带返回值的线程,还可以判断线程是否执行完毕和取消线程。
Thread类将Callable封装成FutureTask,FutureTask又实现了RunnableFuture,RunnableFuture又继承了Runnable,所以本质上还是Runnable,RunnableFuture还继承了Future,Future里面有一些API可以判断线程是否执行完毕和取消线程等。
public class Test {
public static void main(String[] args) {
MyCallable c=new MyCallable();
FutureTask task=new FutureTask(c);
new Thread(task).start();
}
}
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<1000;i++){
sum+=i;
}
return sum;
}
}
- 线程池
一般在创建后,线程池里面的线程池数量为0,在有任务来的时候才创建。
ThreadPoolExecutor poll=new ThreadPoolExecutor(3,
5,
2000,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(5),
new ThreadPoolExecutor.AbortPolicy()
);
corePollSize:核心线程数量
maximumPollSize:最大线程数量,核心线程+非核心线程
keepAliveTime:非核心线程存活的最大空闲时间,也就是在没有任务的情况下非核心线程的最大存活时间
TimeUnit:存活的时间单位
workQueue:阻塞队列,用来存放任务
ThreadFactory:生产线程的工厂
handler:拒绝策略,有4种:
AbortPolicy:直接丢弃,并抛出异常
DiscardPolicy:直接丢弃,但并不会抛异常
DiscordOldestPolicy:丢弃队头任务,然后尝试执行
CallerRunsPolicy:调用线程去执行
执行过程:当有任务来的时候,线程池查看自己是否还有空闲的核心线程,如果有就让核心线程去执行任务,如果没有,就尝试把任务放到工作队列里,如果能放下就放,如果放不下就看现在的线程数是否已经到达最大线程数量,如果没有达到就创建线程去执行,如果已经到达了最大线程数就采取拒绝策略。
还可以通过Exector来创建:
ExecutorService e1=Executors.newSingleThreadExecutor();
ExecutorService e2=Executors.newCachedThreadPool();
ExecutorService e3=Executors.newFixedThreadPool(4);
线程常用API
//Thread调用为静态方法,thread调用为实例方法
Runnable r=()-> System.out.println("线程");
Thread thread=new Thread(r);
//每个线程都有唯一的id
long id=thread.getId();
//获取线程名字
String name=thread.getName();
//设置线程名字
thread.setName("线程2");
//获取线程优先级,优先级高的有可能会被优先执行,但不是一定会被先执行
int priority=thread.getPriority();
//设置线程优先级
thread.setPriority(2);
//获取线程状态,线程转态是个枚举类
Thread.State s=thread.getState();
//NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATED
for(Thread.State state:Thread.State.values()){
System.out.println(state);
}
//判断线程是否是守护线程,jvm会在一个进程的所有非守护线程都结束后才会关闭
//比如:GC就是守护线程
boolean isD=thread.isDaemon();
//判断线程是否存活
boolean isA=thread.isAlive();
//当前活跃的线程数量
Thread.activeCount();
//线程让步
Thread.yield();
//让线程睡眠
Thread.sleep(3000);
//等线程结束
thread.join();
/*
* interrupt()并不会立刻停止一个线程,而是设置一个中断标志位,如果当前线程
* 因为调用了wait(),join(),sleep()等而处于阻塞状态,调用interrupt()就会
* 抛一个异常,清空所有标志位,如果当前线程并没有处于阻塞状态,就会设置一
* 个中断标志位。
* interrupted() 判断是否设置中断标志位,如果设置了会清空中断标志位
* isInterrupted() 判断是否设置中断标志位,如果设置了并不会清空中断标志位
* */
thread.interrupt();
Thread.interrupted();
thread.isInterrupted();
/*
* wait(),notify(),notifyAll() 只能用于同步代码块或同步方法中,用于线程间的通信
* 调用wait(),当前线程释放对象锁,并处于阻塞状态,阻塞在wait()代码行处,等其它线程
* 调用当前对象的notify()或notifyAll()才能被唤醒,也不是立刻就被唤醒,需要等调用
* notify()或notifyAll()的线程释放对象锁之后才能被唤醒
*
* notify()随机唤醒一个线程,notifyAll()唤醒所有线程
*
* */
Object o=new Object();
synchronized(o){
o.wait();
o.notify();
o.notifyAll();
}