一,基础概念
1,CPU核心数和线程的关系
CPU核心数:最早的cpu是单核的。后来出现了多核cpu(2核,4核)
CPU和线程的个数是1:1的关系。比如4核可以允许4个线程同时运行。后来intel提出了超线程的概念。使cpu和线程个数1:2。
2,CPU时间片轮转机制
给每一个进程分配一个时间段,这个时间段就被称为进程的时间片 ---> 这个进程允许运行的时间。
不同进程在cpu上执行,cpu需要进行不同进程之间的切换。每次切换需要耗费5000-20000个时钟周期。这其实是浪费了cpu的资源。
我们在开发时,尽量减少让cpu去进行进程间的切换。
3,什么是线程和进程
进程:程序运行进行资源分配的最小单位。一个进程内部有多个线程,多个会共享这个进程的资源
线程:CPU调度的最小单位,线程不拥有资源。线程依附于进程。
4,并行和并发
并行:某一个时间点,可以处理的事情(同一时刻,可以处理事情的能力)
并发:与时间单位相关,某个时间段内,可以处理的事情(单位时间内可以处理事情的能力)
二,认识Java里的线程
1,Java里的程序天生就是多线程的,比如我们执行main方法时,并不是只有main这个主线程,还有别的线程在运行:
/** * java天生就是多线程的 */ public class OnlyMain { public static void main(String[] args) { //Java虚拟机线程管理的接口。 ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); //通过这个类可以拿到当前应用程序有多少个线程 ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,false); for (ThreadInfo threadInfo:threadInfos){ System.out.println("["+threadInfo.getThreadId()+"] "+threadInfo.getThreadName()); } /** * 打印结果:说明执行main方法时至少启动了5个线程 [5] Monitor Ctrl-Break [4] Signal Dispatcher [3] Finalizer [2] Reference Handler [1] main main方法线程 */ } }
2,启动新线程的三种方式:
/** * 创建线程的三种方式 */ public class NewThread { /** 方式一:扩展自Thread类 */ private static class UseThread extends Thread{ @Override public void run() { System.out.println("i am extends Thread"); } } /** 方式二:实现Runnable */ private static class UseRun implements Runnable{ @Override public void run() { System.out.println("i am implements Runnable"); } } /* 方式三:实现Callable接口,允许有返回值 */ private static class UseCall implements Callable<String>{ @Override public String call() throws Exception { System.out.println("i am implements Callable"); return "CallResult"; } } public static void main(String[] args) throws InterruptedException,ExecutionException{ //Thread启动线程 UseThread useThread = new UseThread(); useThread.start(); //Runnable启动线程 UseRun useRun = new UseRun(); new Thread(useRun).start(); //Callable启动线程 UseCall useCall = new UseCall(); /** * 注意: * 1,Callable是不能直接交给Thread的 * 2,可以把Callable包装成Runnable。FutureTask实现了Runnable接口 * 3,包装成Runnable后,交给Thread */ FutureTask<String> futureTask = new FutureTask<String>(useCall); new Thread(futureTask).start(); //我们可以从Callable拿到返回值。注意:get()方法是阻塞的 String result = futureTask.get(); System.out.println(result); } }
3,Java提供了Thread类,为什么还要提供Runnable接口?
从面向对象的角度思考:Java是单继承的,提供Runnable接口可以多实现。