线程基础知识

发布时间:2023年12月27日

多线程的基本知识

一、理解线程、进程、程序之间的区别

  1. 线程 :线程与进程相似,但是线程是比进程更小的执行单位,一个进程在其执行过程中可以产生多个线程,与进程不同是同类的多个线程是共享一块内存空间和一组系统资源 或者是线程之间切换的开销比进程小很多,所以线程又被成为轻量级进程。
  2. 进程: 是程序的一次执行过程,是系统程序运行的基本单位,所以进程是动态的。直白就是一个系统程序的运行就是一个进程从创建、运行、消亡的过程。换句话说当程序在被执行的时候,将会被操作系统放进内存中,线程是进程划分为的更小的运行单位,线程和进程最大的不同点就是各进程基本上是相互独立的,但是线程是不一定的,同一进程中的线程可能是会互相影响的(修改数据,死锁等)
    3.程序:程序是含有指令和数据的文件,被存储在介质中,是静态的代码文件。

二、线程的生命周期,线程状态等基本概念

线程生命周期图
1.NEW: 表示线程刚刚创建,还没有进行运用
2.RUNNABLE :运行状态,当调用start()方法时候,处于该状态,表示线程所需要的一切都准备好了。
3. BLOCKED:阻塞状态,是当线程遇到了锁,那么就会进入到这个状态。
4. WAITING:等待中,① 当前线程A执行了A.wait(),则会将当前线程转换为WAITING状态,当其他线程(比如线程B)执行B.notify()方法或B.notifyAll()方法后,则会将当前线程转换为RUNNABLE状态;② 当前线程A执行了其他线程(比如线程B)的B.join()方法,则当前线程会进入WAINTING状态,等到执行了join()方法的线程(比如线程B)执行结束,当前线程会进入RUNNABLE状态;③ 在当前线程(比如线程A)中,执行了LockSupport工具类的park()方法,则当前线程A会进入WATING状态,等到其他线程(比如线程B)执行了LockSupport工具类的B.unpark(A)方法,则当前线程A会重新进入RUNNABLE状态。
5. TIMED_WAITNG(超时等待): 该状态不会进行无期限的等待,当超过限定时间会自动变为RUNABLE状态,比如是调用带时间的wait,sleep方法
6. TERMINATED终止状态:线程结束,处于终止状态。

三、线程的常用API方法

线程常用方法

  • wait(): 当线程A调用一个共享变量的wait()方法时候,线程A就会被挂起,只有当线程调用notify()或者notifyAll()方法才会被唤醒;或者其他线程调用了A的interrupt()方法,线程A抛出interruptedException异常返回。
  • wait(long time) :不同于wait()多了一个超时时间,到了时间会因为超时而返回。
  • wait(int time,int nanos):内部调用的是wait(int timeout)函数
  • 唤醒主要是靠:notify()和notifyAll()方法;notify唤醒单个,notifyAll唤醒所有的用于该共享变量上的所有wait方法。
  • Thread类也提供了一个用于等待的方法join(),如果一个线程A执行了Thread。join()方法,含义是当前线程A等待Thread执行终止后才从thrad.join()中返回。
  • 让出优先权yield() :Thread类中的静态方法,当一个线程调用yield,实际上就是说我当前不用cpu,可以先给其他线程,但是线程调度器可以无视这个请求。
  • 线程中断
    线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态之行处理。
    • interrupt ():中断线程,列如线程A在运行的时候,线程B可以执行interrupt来设置线程的中断标志位true并立即返回,但是设置标志仅仅只是标志,线程A时间并没有被中断,会继续执行。并不等于是阻塞。
  • boolean isInterrupted() 方法: 检测当前线程是否被中断。
  • boolean interrupted() 方法: 检测当前线程是否被中断,与 isInterrupted 不同的是,该方法如果发现当前线程被中断,则会清除中断标志。
  • 休眠sleep(long time):Thread类中的静态方法,当执行器A调用了sleep方法后,线程A会让出指定的时间的执行权,但是线程A还是有监视器资源,比如锁是持有还是释放,指定时间到了后该函数会正常返回,接着参加cpu的调度,获取cpu资源后可以继续运行。

四、线程如何创建、启动、停止

线程主要有三种创建方式:继承Thread类,实现Runnable接口,实现Callable接口

  • 继承ThRead类,重写run方法,实现start()启动线程
public class ThreadTest {
/**
* 继承Thread类
*/
public static class MyThread extends Thread
			{ @Override
			public void run() {
			System.out.println("This is child thread");
			}
}
public static void main(String[] args) {
		MyThread thread = new MyThread();
		thread.start();
		}
}
  • 实现Runnable方法,重写run方法
public class RunnableTask implements Runnable {
			public void run()
				{ System.out.println("Runnable!");
				}
			public static void main(String[] args) {
				RunnableTask task = new RunnableTask();
				new Thread(task).start();
				}
}

上面两种都是没有返回值的

  • 实现Callable接口,重写call方法,这种可以通过FutureTask获取任务执行的返回值
public class CallerTask implements Callable<String> {
	public String call() throws Exception
		{ return "Hello,i am running!";
		}
	public static void main(String[] args) {
		//创建异步任务
		FutureTask<String> task=new FutureTask<String>(new CallerTask());
		//启动线程
		newThread(task).start(); try {
		//等待执行完成,并获取返回结果
		String result=task.get(); System.out.println(result);
		} catch (InterruptedException e)
		{ e.printStackTrace();
		} catch (ExecutionException e)
		{ e.printStackTrace();
		}
}
}

为什么调用start()方法会执行run()方法,而不去直接调用run方法:这是因为jvm会执行start()方法,是先创建一个线程,由创建出来的新线程去执行thread的run()方法,这样才会起到多线程作用。如果我们直接调用run方法,那么还是运行在主线程中,相当于顺序执行,起不到多线程作用。
在这里插入图片描述

下一章我们在接着说同步和锁,并发等。
ps: 希望大家一起参与学习,一起进步,留下评论,一起讨论下啊

文章来源:https://blog.csdn.net/weixin_45003206/article/details/135231744
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。