基础概念
CPU 核心数和线程数的关系
核心数:线程数 = 1:1 ,后面使用了超线程技术后--》1:2
CPU 时间片轮转机制
又称RR调度,会导致上下文切换
什么是进程和线程
进程:程序运行资源分配的最小单元,进程内部有多个线程会共享这个进程。
线程:CPU调度的最小单位,必须依赖进程而存在。
并发(concurrency)和并行(parallelism)
并行:同一时刻,可以同时处理事情的能力。
并发:于单位时间相关,在单位时间内可以处理事情的能力。
解释:1.并行是指两个或者多个事件在同一时刻发生,而并发是指两个或者多个事件在同一时间间隔发生。
2.并行是在不同实体上的多个事件,并发是在同一个实体上的多个事件。
3.在一台处理器上“同时”处理多个任务。在多态处理器上同时处理多个任务。如 hadoop 分布式集群。
所以并发编程的目标是充分利用处理器上的每一个核,以达到最高处理性能。
并发编程的意义,好处,和注意事项
好处:
1.充分利用cpu 的资源
2:加快用户响应的事件
3:程序模块化,异步化,可以用多线程解藕。
问题:
1:线程共享资源,存在冲突,容易导致死锁。
2:启动太多的线程,就会有搞垮机器的可能。
认识JAVA里面的线程
java 天生就是多线程的,启动一个main 就要启动最少5个线程
上代码:
/**
* @author sxylml
* @Date : 2019/2/25 15:35
* @Description: java 线程,就算只启动一个main线程,JVM 也会启动其它5-6个线程
*/
public class OnlyMain {
public static void main(String[] args) {
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (int i = 0; i < threadInfos.length; i++) {
System.out.println(threadInfos[i].getThreadId()+"-"+ threadInfos[i].getThreadName());
}
}
}
运行结果:
Attach Listener :线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者。通常我们会用一些命令去要求jvm给我们一些反馈信息,如:java -version、jmap、jstack等等。如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动。
signal dispather: 前面我们提到第一个Attach Listener线程的职责是接收外部jvm命令,当命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。signal dispather线程也是在第一次接收外部jvm命令时,进行初始化工作。
Finalizer: 用来执行所有用户Finalizer 方法的线程
Reference Handler :它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。
JAVA 新启动线程的三种方式:
package com.cap1.tools;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author sxylml
* @Date : 2019/2/25 15:42
* @Description: 创建线程的三种方式DEMO
*/
public class NewThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
InteriorThread useThread = new InteriorThread();
useThread.setName("staticInteriorThread");
useThread.start();
OuterUseThread outerThread = new OuterUseThread();
outerThread.setName("OuterUseThread");
outerThread.start();
// 继承runable 接口
new Thread(new UseRunnable()).start();
//
UseCallable useCallable = new UseCallable();
FutureTask futureTask = new FutureTask(useCallable);
new Thread(futureTask).start();
// 可以输出返回参数
System.out.println(futureTask.get());
}
/**
* 1:继承Thread 类
* 因为非静态内部类对 main 方法而言是不直接可见的
*/
private static class InteriorThread extends Thread {
@Override
public void run() {
System.out.println("线程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm extends Thread");
}
}
/**
* 2:实现Runnable 接口
*/
private static class UseRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm implements Runnable");
}
}
/**
* 3 实现 Callable 接口是允许有返回类型的,类型就是接口《泛型》
*/
private static class UseCallable implements Callable {
@Override
public String call() throws Exception {
System.out.println("线程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm implements Callable");
return "CallableResult";
}
}
}
class OuterUseThread extends Thread {
@Override
public void run() {
System.out.println("线程信息 ID:[ "+Thread.currentThread().getId()+"] name: ["+Thread.currentThread().getName()+ "] I'm extends Thread");
}
}
运行结果:
JAVA线程的生命周期:
新建状态(New):当线程对象对创建后,即进入了新建状态,如: InteriorThread t = new InteriorThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程的 run,start 方法
/**
* @author sxylml
* @Date : 2019/2/25 16:27
* @Description:
*/
public class StartAndRun {
private static class RunThread extends Thread {
@Override
public void run() {
int i = 5;
while (i > 0) {
// 休眠一千毫秒
SleepTools.ms(100);
System.out.println("i'm " + Thread.currentThread().getName() + " i=" + i--);
}
}
}
public static void main(String[] args) {
Thread beCalled = new RunThread();
beCalled.setName("beCalled");
beCalled.run(); // 面向对象,这样直接调用run 和普通类一样 : run 归谁调用,就归那个线程,這样运行结果就是main
beCalled.start();// 只有调用start() ,才会是线程
}
}
守护线程:
public class DaemonThread {
private static class UseThread extends Thread {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
while (!isInterrupted()) {
System.out.println(threadName + " extend Thread");
// 抛出异常
Thread.sleep(100);
}
// 抛出异常這代码就不执行咯
System.out.println(threadName + " interrupted " + isInterrupted());
} catch (Exception e) {
e.printStackTrace();
System.out.println(threadName + " try interrupted " + isInterrupted());
Thread.currentThread().interrupt();
} finally {
// 是守护线程的的情况,不一定执行
System.out.println("-------------finally -----------------");
}
}
}
public static void main(String[] args) throws InterruptedException {
UseThread useThread = new UseThread();
// 守护线程 和主线程共死,finally不能保证一定执行
useThread.setDaemon(true);
useThread.start();
Thread.sleep(1000);
// 发送中断通知
useThread.interrupt();
}
}
如何正确的终止线程
1:线程自然终止:自然执行完毕或抛出未处理异常
stop(),resume(),supende() 已经不建议使用,stop()会导致线程不会正确释放资源,suspend()容易导致死锁。
2:调用interrupt() 方法中断一个线程,并不是强行关闭這个线程,只是跟這个线程打个招呼,讲线程的中断标志设置为true,线程是否中断,由线程本身决定。
isInterrupted() 判断当前线程是否处于中断状态。
static 方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false.
方法如果抛出interruptedException ,线程的中断标志位会被复位或成false ,如果确实是需要中断线程,要求我们自己在catch 语句块里再次调用interrupt().
/**
* @author sxylml
* @Date : 2019/2/25 15:52
* @Description: 如何正确的中断线程
*/
public class EndThread {
private static class UseThread extends Thread {
public UseThread(String name) {
super(name);
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
// 不是中断的时候,就循环执行
while (!isInterrupted()) {
// 当没有向线程发送中断请求,这执行循环里面的值
System.out.println(threadName + " is run in while ! in interrupt flag is " + this.isInterrupted());
}
// 当发送了中断请求,并且中断标识改变了,则退出循环
System.out.println(threadName + " is run out while ! in interrupt flag is " + this.isInterrupted());
}
}
public static void main(String[] args) {
Thread endThread = new UseThread("endThread");
endThread.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("向线程发送中断请求!");
endThread.interrupt();
}
}
public class EndRunnable {
private static class UseRunable implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
while (!Thread.currentThread().isInterrupted()) {
// 当没有向线程发送中断请求,这执行循环里面的值
System.out.println(threadName + " is running in while ! interrupt flag is " + Thread.currentThread().isInterrupted());
}
// 当发送了中断请求,并且中断标识改变了,则退出循环
System.out.println(threadName + " is run out while ! interrupt flag is " + Thread.currentThread().isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
UseRunable useRunable = new UseRunable();
Thread thread = new Thread(useRunable, "endRunnable");
thread.start();
Thread.sleep(20);
System.out.println("发起中断请求!");
thread.interrupt(); // 发起中断信号
}
}