线程基础
程序、进程与线程
程序
程序是一组指令的集合
进程
- 进程是程序的以此静态态执行的过程,占用特点的地址空间
- 每个进程都是独立的,是资源分配的最小单位占用一定的cpu资源和内存空间。
- 进程的创建需要系统分配额外的cpu资源和内存空间,销毁时系统将回收为其分配的内存空间
- 进程间的通信需要建立额外的通信机制(如:管道、消息队列、系统信号),而且不同进程间的切换还会触发CPU中断程序保存进程的CPU执行状态来切换到其他进程的执行,而后再切换回来时将从内存中恢复上一次的执行的CPU状态进而继续执行
线程
- 线程又被称为轻量级进程
- 一个进程可以拥有多个并行的线程
- 一个进程中的线程共享进程中的内存空间,所以可访问相同的变量和对象,而且它们从同一个堆中分配对象进而可以进行线程间通信、数据交换及线程同步
- 因为线程间的通信是在同一地址空间上进行的,所以不需要额外的通信机制(如:管道、消息队列、系统信号),这就使得通信更便捷
- 线程是CPU调度的最小单位,即一个多核cpu的其中一个核最小能同时处理一个线程(此处不讨论CPU的超线程技术)
进程和线程的区别
线程的创建与启动
方法一:
1.需要继承Thread类
2.必须要重写run方法,指的是核心执行的逻辑
3.线程在启动的时候,不要直接调用run方法,而是要通过start()来进行调用
4.每次运行相同的代码,结果可能不一样,原因在于多线程谁先抢占资源无法进行人为控制
public class ThreadDemo extends Thread{
@Override
public void run() {
for(int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--------------"+i);
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.start();
for(int i =0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"==========="+i);
}
}
}
方法二:
1.实现Runnable接口
2.重写run方法
3.创建Thread对象,将刚刚创建好的runnable的子类实现作为thread的构造参数
public class RunnableDemo implements Runnable {
@Override
public void run() {
for(int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--------------"+i);
}
}
public static void main(String[] args) {
RunnableDemo runnableDemo = new RunnableDemo();
Thread thread = new Thread(runnableDemo);
thread.start();
for(int i =0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"==========="+i);
}
}
}
推荐使用第二种方式:
原因
1.java是单继承,将继承关系留给最需要的类
2.使用runnable接口之后不需要给共享变量添加static关键字,每次创建一个对象,作为共享对象即可
3.第二种方法实际上使用了**代理模式**
线程的生命周期
-
- 新生态:
当创建好当前线程对象之后,没有启动之前(调用start方法之前)
ThreadDemo thread = new ThreadDemo()
RunnableDemo run = new RunnableDemo()
- 新生态:
-
- 就绪态:准备开始执行,并没有执行,表示调用start方法之后 当对应的线程创建完成,且调用start方法之后,所有的线程会添加到一个就绪队列中,所有的线程同时去抢占cpu的资源
-
- 运行态:当当前进程获取到cpu资源之后,就绪队列中的所有线程会去抢占cpu的资源,谁先抢占到谁先执行,在执行的过程中就叫做运行状态抢占到cpu资源,执行代码逻辑开始
-
-
死亡态:当运行中的线程正常执行完所有的代码逻辑或者因为异常情况导致程序结束叫做死亡状态
进入的方式:1.正常运行完成且结束 2.人为中断执行,比如使用stop方法 3.程序抛出未捕获的异常
-
-
-
阻塞态:在程序运行过程中,发生某些异常情况,导致当前线程无法再顺利执行下去,此时会进入阻塞状态,引起阻塞状态的因素消除之后,所有的阻塞队列会再次进入到就绪状态中,随机抢占cpu的资源,等待执行
进入的原因或方式:1.sleep方法 2.等待io资源 3. join方法(代码中执行的逻辑)
-
注意:
线程相关操作方法
public class ThreadApiDemo implements Runnable{
public static void main(String[] args) {
//获取当前线程对象
Thread thread = Thread.currentThread();
//获取当前线程的名称
System.out.println(thread.getName());
//获取线程的id
System.out.println(thread.getId());
//获取线程的优先级,在一般系统中范围是0-10的值,如果没有经过设置的话,就是默认值5,有些系统是0-100
System.out.println(thread.getPriority());
//设置线程池的优先级
/*
* 优先级越高一定越先执行吗?
* 不一定,只是优先执行的概率比较大而已
* */
thread.setPriority(6);
System.out.println(thread.getPriority());
ThreadApiDemo threadApiDemo = new ThreadApiDemo();
Thread t1 = new Thread(threadApiDemo);
System.out.println(t1.isAlive());
t1.start();
System.out.println(t1.isAlive());
System.out.println(t1.getPriority());
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"-----"+i);
}
System.out.println(t1.isAlive());
}
@Override
public void run() {
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"-----"+i);
}
}
}
MyRun类
public class MyRun implements Runnable {
@Override
public void run() {
for(int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"------"+i);
}
}
}
join案例
public class JoinTest {
public static void main(String[] args) {
MyRun run = new MyRun();
Thread thread = new Thread(run);
thread.start();
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"-----------------"+i);
if(i==3){
try {
thread.join();//阻塞当前线程,直到其他线程执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
sleep案例
public class SleepTest {
public static void main(String[] args) {
MyRun run = new MyRun();
Thread thread = new Thread(run);
thread.start();
for(int i = 0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"=============="+i);
if(i==2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
yield及stop案例
public class YieldTest {
public static void main(String[] args) {
MyRun run = new MyRun();
Thread thread = new Thread(run);
thread.start();
for(int i = 0;i<5;i++){
if(i==2){
Thread.yield();//当前main线程让出CPU,而后马上进入就绪态,等待cpu调度
// thread.stop();//停止thread启动的线程
System.out.println(Thread.currentThread().getName()+"=============="+i+"让出CPU一次");
}else{
System.out.println(Thread.currentThread().getName()+"=============="+i);
}
}
}
}
下篇Java 多线程之旅2-线程同步
本文完,下篇将介绍线程同步及生产者消费者案例