目录

  • 线程概念
  • 线程的生命周期
  • 线程的实现方式
  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口
  • 线程的调度与优先级
  • 优先级
  • 调度
  • 线程的数据安全
  • synchronized关键字
  • 守护线程与定时器
  • 线程的常用方法
  • 线程池创建方式



线程概念

进程是一个应用程序,线程是一个进程中的执行场景/执行单元,一个进程可以启动多个线程。java程序执行过程中,至少有两个线程并发,一个是执行程序的主线程,一个是垃圾回收线程。进程之间内存独立不共享,线程之间堆内存和方法区内存共享,但栈内存独立,一个线程一个栈,Java中多线程并发可以提高效率。注:单核CPU不能真正实现多线程并发,只是cpu执行速度很快,多个线程之间频繁切换执行,感觉像多线程并发。

线程的生命周期

线程的生命周期:一个线程从创建、启动、执行到停止的完整过程。线程的生命周期中也可产生各种线程状态,如下图:

java 线程 cpu 线程 java线程和cpu线程_开发语言

(图片来源网络)

线程状态说明:

  • 初始状态:线程创建完成后进入此状态。此时线程未获得CPU使用权。
  • 可执行状态:线程调用start()方法启动线程进入此状态。此时线程需争夺CPU使用权,获得CPU使用权的线程执行run()方法,未获得的需排队。
  • 不可执行状态:线程调用了wait()方法、在当前线程中调用另一个线程的join()方法、调用sleep()方法都可进入此状态。此时线程获得的CPU使用权会失去。
  • 终止状态:线程执行完成调用stop()方法进入此状态。此时CPU使用权会交出。

线程的实现方式

注意点:start()方法是native方法,JVM会另起一个线程执行run()方法,而直接执行run()方法是本地线程执行。

继承Thread类

步骤:定义一个类继承Thread类,在类中重写run()方法,创建类对象,调用start()方法启动线程。

public static class Test extends Thread{
	@Override
	public void run() {
		System.out.println("继承Thread类");
	}
}
public static void main(String[] args) {
	new Test().start();
}

实现Runnable接口

步骤:定义一个类实现Runnable接口,在类中重写run()方法,创建类对象,创建Thread类的对象并将类对象作为Thread类构造参数,Thread类对象调用start()方法启动线程。

public static class Test implements Runnable{
	@Override
	public void run() {
		System.out.println("实现Runnable接口");
	}
}
public static void main(String[] args) {
	new Thread(new Test()).start();
}

实现Callable接口

步骤:定义一个类实现Callable<类型>接口,在类中重写call()方法(此方法含返回值与接口泛型类型一致),创建类对象,创建Future的实现类FutureTask对象并将类对象作为其构造参数(Thread类构造只能接受Runnable实现类对象,不能接受Callable实现类对象), 创建Thread类的对象并将FutureTask类对象作为Thread类构造参数,Thread类对象调用start()方法启动线程,调用get方法获取线程结束之后的结果。

public static class Test implements Callable<String>{
	@Override
	public String call() throws Exception {
		return "实现Callable接口";
	}
}
public static void main(String[] args) throws Exception {
    Test t = new Test();
    FutureTask<String> ft = new FutureTask<>(t);
    Thread t1 = new Thread(ft);
    t1.start();
    // get()一定要在start()之后
    String s = ft.get();
    System.out.println(s);
}

线程的调度与优先级

优先级

在多线程中,每个线程都被赋予一个优先级,优先级决定CPU执行顺序。其最低优先级为1,默认优先级为5,最高优先级为10。大概率下优先级较高的获取CPU时间片会更长。其获取与设置优先级方法如下:

方法

说明

void setPriority(int newPriority)

设置线程新的优先级为newPriority

int getPriority()

获取线程的优先级

调度

线程调度:在多线程中,如果当前线程在执行中,拥有更高优先级线程进入可执行状态,此时这个更高优先级线程会被立即调度执行。调度分为两种方式,如下:

  • 分时方式:CPU资源按时间分配,执行一个线程只能在其分配的时间内执行,一旦超出时间,则将等待下一个时间片的调度执行。
  • 独占方式:当线程获取执行权后将一直执行,直到线程执行完毕或由于某种原因(调用yield()、sleep()、wait()方法,用户操作,程序异常等)放弃CPU资源。

线程的数据安全

出现数据安全问题条件:多线程并发,有共享数据,共享数据有修改行为。
易出现线程问题的变量:实例变量(堆)和静态变量(方法区),因为堆和方法区都是多线程共享。

synchronized关键字

synchronized关键字:线程同步锁。会降低程序执行效率。其写法如下:

第一种:在同步代码块中使用
synchronized(线程共享对象){
同步代码块;
}

第二种:在静态方法上使用
表示找类锁,类锁永远只有1把。

第三种:在实例方法上使用
表示共享对象是this,并且同步代码块为整个方法体。

总结:
synchronized最好不要嵌套使用,容易出现死锁。
对象锁:1个对象1把锁,n个对象n把锁。
类锁:n个对象可能就1把锁。

解决数据安全问题:线程同步,线程需要排队执行(不能并发),会牺牲部分效率,但数据是安全的。开发中解决方案如下:

  • 尽量使用局部变量代替实例变量和静态变量,因为局部变量属于栈,栈不共享,所以局部变量不会存在线程安全问题。
  • 如果不能使用局部变量,可考虑创建多个对象,因为1个对象1把锁,对象不共享。
  • 当前两种都不能使用时,则使用synchronized关键字。

守护线程与定时器

守护线程:线程默认为非守护线程,也称用户线程。守护线程可随时结束并且不影响运行结果。当用户线程结束时,守护线程也会立即结束。
最具代表性守护线程:垃圾回收线程。
设置守护线程的方法:线程对象.setDaemon(true);

定时器:间隔特定的时间,执行特定的程序。
定时器的实现方式:

  • sleep()方法:设置特定的睡眠时间,到时间后执行任务。
  • java.util.Timer类库:可以直接使用其相关方法执行任务,使用较少。
  • SpringTask框架:只需简单配置即可完成定时任务。

线程的常用方法

线程的常用方法

说明

wait()

线程等待,让线程进入Waiting(无限等待)状态,线程会释放对象锁,一般用在同步方法或同步代码块中。在Object类中

sleep()

线程休眠,让线程进入Time_Waiting(带时间限制的Waiting)状态,需传入一个参数标识线程需要休眠的时间

yield()

线程让步,使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争CPU 时间片

interrupt()

线程中断,如果线程处于Running(执行)状态,只会改变中断标识位,不会真的中断正在运行的线程。如果线程当前处于Timed_Waiting(带时间限制的Waiting)状态,则会让线程抛出InterruptedException异常

join()

在当前线程中调用另一个线程的join()方法,则当前线程转为阻塞状态

notify()

唤醒在对象上等待的单个线程,如果有多个线程在等待,选择其中一个进行唤醒。线程唤醒后不会立即执行,等当前线程执行结束释放锁后,被唤醒线程获得对象锁之后才开始执行。在Object类中

notifyAll()

唤醒在对象上等待的所有线程。在Object类中

线程池创建方式

在Java中,并发编程通常都使用创建线程池实现,线程池创建主要分为两大类:通过ThreadPoolExecutor手动创建线程池和通过Executors 执行器自动创建线程池。其具体实现方法如下:

方法

说明

Executors.newFixedThreadPool

创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待

Executors.newCachedThreadPool

创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程

Executors.newSingleThreadExecutor

创建单个线程数的线程池,它可以保证先进先出的执行顺序

Executors.newScheduledThreadPool

创建一个可以执行延迟任务的线程池

Executors.newSingleThreadScheduledExecutor

创建一个单线程的可以执行延迟任务的线程池

Executors.newWorkStealingPool

创建一个抢占式执行的线程池(任务执行顺序不确定)

ThreadPoolExecutor

手动创建线程池的方式,它创建时最多可以设置 7 个参数