Java中的线程:
程序、进程和线程
1程序是一段静态的代码,它是应用程序执行的蓝本
2进程是程序的一次动态执行过程。它对应了从代码加载、执行到执行完毕的一个完整过程。作为蓝本的程序可以被多次加载到系统的不同内存区域分别执行,形成不同的进程。
3线程是进程内部单一的一个顺序控制流。一个进程在执行过程中,可以产生多个线程。每个线程也有自己产生、存在和消亡的过程。
使用线程的目的就是 增加并发性。
就绪态:1分配指令计数器pc 2在jvm中拥有独立的虚拟机栈、本地方法栈和PC寄存器(可以随时占用处理机)会跟主线程争夺处理机 主方法执行完毕 就主线程就执行完 jvm中就绪池中还有两个线程
处于阻塞态的资源 :释放jvm的空间 不会征用处理机
开始状态线程 :在jvm中开辟空间
(1) Java线程的调度策略 依赖系统的调度策略 没有规律可言!
① 随机调度(随机从就绪态中拿一个)
② 一旦线程在执行,只有当前线程执行完或者执行到某个可停止点,才会调度其他线程。
③ C语言,windows系统底层:先来先服务,时间片轮转
后台线程:前台线程执行完毕 后台线程将自动结束
线程是不安全的: 会出现“100-40=80”的情况 所以需要尽可能避免这种情况currentThread当前线程对象的地址
(1) currentThread.getName:当前线程对象的线程名
(2) mathine1.setName(“m1”);设置线程名
(3) SetPriority:设置优先级
① 优先级:有三种
- 十级:MAX_PRIORITY
- 五级:默认NORM_ PRIORITY=5
- 一级:MIN_PRIORITY
② 由主线程创建的线程默认和主线程相同
- Yield():(当某一个线程执行,不会主动放弃处理机)主动放弃处理机,从运行态直接进入就绪态
(1) 当某个线程占用占用处理机,主动放弃处理机,给同优先级或更高优先级的线程占用处理机的机会,从运行态直接进入就绪态。 - setDaemon():设定为后台线程(必须在该线程启动(start())之前)
(1) 所有前台进程结束后,后台线程自动终止
public class Mathine extends Thread{
public void run(){
for ( int a=0;a<10;a++){
System.out.println(currentThread().getName()+":"+a);//拿到线程的地址 然后再拿到线程的名字
try { //阻塞态条件满足后 会处于就绪态
sleep(100);//休眠期 处于阻塞态:1主动放弃处理机sleep 2 等待io的操作 等待用户输入数据 3执行某段代码的时候发现代码上锁了 4处于对象阻塞池
}catch (InterruptedException e) {throw new RuntimeException(e);
}
}
}
public static void main(String[] args) {//通过main方法进行主线程,在栈中开辟主方法栈帧空间开辟主线程的run方法栈帧。主方法执行完毕,主线程也会结束。
Mathine mathine1 = new Mathine();//在栈区 给mathine1线程 开辟 run方法栈帧
Mathine mathine2 = new Mathine();//在栈区 给mathine1线程 开辟 run方法栈帧
//mathine1.setName("m1");
// mathine2.setName("m2");
mathine1.setPriority(Thread.MAX_PRIORITY);//设置线程的优先级 高优先
mathine2.setPriority(Thread.MIN_PRIORITY);//设置线程的优先级 低优先
// System.out.println("Priority of m1:"+mathine1.getPriority());
// System.out.println("Priority of m2:"+mathine2.getPriority());
mathine1.start(); //执行mathine1线程 栈的run()方法空间 此时处于就绪状态(1在栈中分配了空间 ,2分配指令计数器pc,可以随时占用处理机)
mathine2.start();//mathine2线程栈的 run方法空间 此时处于就绪状态
mathine1.run(); //开辟主线程的run方法栈帧。主线程主动执行了 mathine1的run 方法 Thread.currentThread().getName()//得到线程地址跟线程名
线程的3中常用创建方式
第一种:继承Thread这个类 , 然后复写该类的run方法, 通过new 该类 调用start方法来开启线程。
第二种:
**
第三种:通过自定义线程池来调用
线程池可以自定义创建 也可以直接调用API创建
我们通常通过ThreadPoolExecutor来创建线程池,具体细节就不细说了
通过调用execute()方法来开启线程
这三种创建线程的方式推荐通过线程池,因为通过线程池固定了线程的数量,不会因为线程大量的新生死亡操作而消耗CPU。
这两个方法的区别:如果直接调用线程的run()方法,会在当前线程执行,调用线程的start方法则会在新的线程执行。
为什么会出现线程不安全的问题
对于多个线程操作堆中的同一个对象,因为线程是独立的,对象是共享的,而且JVM采用了分片方式,即每个线程只会执行jvm给你分配的时间片,如果时间耗尽或者到了安全停止点,那么该线程就会让出资源,给其他线程。这就导致,线程执行语句不会全部执行完,解决方法,让所有执行语句看成一个整体,只有整体执行完毕才会让出资源。即使用 synchronized