线程和进程

每一个进程就是一个应用程序,有自己独立的内存空间。
计算机引入多进程的作用:提高 CPU 的使用率
重点:进程和进程之间的内存独立。

线程是进程中的一个执行场景,一个进程可以启动多个线程。同一个进程中的线程共享其进程中的内存和资源 (共享的内存是堆内存和方法区内存,栈内存不共享,每个线程有自己的。)
计算机引入多线程的作用:提高进程的使用率。
重点:线程和线程之间栈内存独立,堆内存和方法区内存共享。一个线程一个栈。

线程的创建和启动

线程的创建

继承Thread类

image.png

实现 Runnable 接口(推荐使用 Runnable 接口)

其实 Thread 对象本身就实现了 Runnable 接口,但一般建议直接使用 Runnable 接口来写多线程
程序,实现接口之后还有机会去继承其他需要的类,而且Java支持多实现。

线程的启动

通过线程调用.start()方法,不用调用run()方法,线程启动后就会去调用。

注:同步编程模式:没有使用多线程,执行有先后顺序,需要排队等待
异步编程模式:使用多线程,执行没有顺序,不需要排队等待。

线程的生命周期

线程的生命周期存在五个状态:新建、就绪、运行、阻塞、死亡
image.png

线程的调度与控制

线程优先级

线 程 优 先 级 主 要 分 三 种 : MAX_PRIORITY( 最高级 );MIN_PRIORITY (最低级) NOM_PRIORITY(标准)默认
设置优先级要在线程启动前设置,通过方法:.setPriority(Thread.线程级别)
注:获取线程的名字:线程对象.getName();
获取当前线程的名字:Thread.currentThread().getName()

线程睡眠 Thread.sleep

sleep 设置休眠的时间,单位毫秒,当一个线程遇到 sleep 的时候,就会睡眠,进入到阻塞状态,
放弃 CPU,腾出 cpu 时间片,给其他线程用,所以在开发中通常我们会这样做,使其他的线
程能够取得 CPU 时间片,当睡眠时间到达了,线程会进入可运行状态,得到 CPU 时间片继续
执行,如果线程在睡眠状态被中断了,将会抛出 IterruptedException

Thread.yield

它与 sleep()类似,只是不能由用户指定暂停多长时间,并且 yield()方法只能让同优先级的线程有执行的机会

t.join();

当前线程可以调用另一个线程的 join 方法,调用后当前线程会被阻塞不再执行,直到被调用的线程执行完毕,当前线程才会执行

interrupt(中断)

我们的线程正在睡眠,可以采用 interrupt 进行中断

正确的停止一个线程

通常定义一个标记,来判断标记的状态停止线程的执行

  1. //在线程类中定义一个标记变量
  2. private boolean flag;
  3. ...
  4. if(flag) {
  5. //如果flag为真,停止退出
  6. break;
  7. }
  8. ...
  9. public void setFlag(boolean flag) {
  10. this.flag = flag;
  11. }
  12. //在主程序中,给flag设置值;
  13. setFlag (true);

线程同步(加锁问题)

线程安全问题

将一个已经编写好的程序放在一个多线程的环境中,可能会存在数据安全问题,因为共享同一个数据对象,所以不安全,需要进行设置。

存在线程安全问题的3个条件

1.多线程并发
2.有共享数据
3.共享数据有修改行为

解决线程安全问题,要采用“线程同步机制”,
同步代码块

synchronized (线程共享对象) {
    同步代码块;
}

锁的问题

1.对象锁:每个Java对象都有一把锁,这把锁其实只是一个标记,我们把这个标记称为锁
2.类锁:类锁永远只有一把,即使创建了多个对象也只有一把。
3.死锁:不会出现异常,也不会出现错误,程序一直僵持在那里。

线程分类

1.用户线程
2.守护线程:所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束。
线程对象.setDaemon(true); 通过该方法将线程设置为守护线程。