多线程

纵饮孤独 提交于 2020-03-12 05:21:33

多线程

  • 进程
    • 程序执行过程中具有动态性;持有资源和线程,是系统进行资源分配和调度的基本单位
  • 线程
    • 线程的分类
      • 主线程:JVM调用程序mian()所产生的线程。
      • 当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
      • 后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
      • 前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
    • 有时被称为轻量级进程,是程序执行中的最小单元
    • 互斥与交互
  • 线程实现方法
    • 继承Thread类,重写该类的run()方法;
    • 通过实现Runnable接口来创建线程,并重写该线程的run()方法
    • 创建实现Callable和Future创建线程,实现call()方法
  • 创建线程方法的优劣点
    • 采用Runnable()、Callable()接口:
      • 优点:还可以继承其他类,可以实现多个线程共享同一个target对象吗,从而可以将CPU,代码和数据分开,体现了较好的面向对象的思想
      • 劣势:编程较为复杂,如果要访问当前线程需要使用Thread.currentThread()方法
    • 采用继承Thread类的时候:
      • 优势:编程简单,访问当前线程的时候直接使用This方法即可获得当前的线程
      • 劣势:已经继承了Thread类,无法继承其他的父类。
  • 线程的一些问题
    • 线程的名字:自己设置或者虚拟机随机设置。获取当前线程名字的方法:Thread.currentThread().getName()
    • 线程的启动:只有一个新的线程可以被启动(只有一个新线程或者一个死线程),并且一次。当线程的run()方法结束时,该线程结束
  • 线程状态

    • New新建状态:线程状态已创建,没有调用start()方法。
    • Runnable可运行状态:当线程有运行资格,在线程池还没有选定为运行状态,start()调用时,线程进入可运行状态,当线程运行后或者从阻塞状态恢复的时候为可运行状态。
    • Running运行状态:线程调度程序从线程池调用一个线程作为当前运行的线程,这是进入运行状态的唯一方式。
    • Blocked状态:线程因为某种原因放弃CPU使用权,暂时停止运行,进入Runnable状态,当出现某个事件时候,可以回到运行状态。阻塞状态分三种:
      • 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
      • 同步阻塞:运行线程在获取对象的同步锁是,如果同步锁被占用,JVM会把该线程放入锁池中。
      • 其他阻塞:运行的线程执行sleep(),join()方法,或者发出I/O请求是,JVM会把该线程设置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
    • Dead状态:当线程调用完run()方法时为死亡状态,死亡状态的线程再次调用start()时候会报java.lang.IllegalThreadStateException异常

线程的状态转换

  

  • 线程的调度

    • 线程的优先级:优先级高的线程会获得较多的运行机会
      • java线程 取值范围1-10 系统默认的优先级为5
      • Thread.setPriority和getPriority来设置和获取线程的优先级
    • 线程的睡眠:调用Thread.sleep(long millis))方法,使线程回到阻塞状态,毫秒单位睡眠时间,时间结束自定转为Runnable状态。

    • 线程的等待:调用Thread.yield()方法,暂停当前执行的线程对象,把执行的机会让给相同或者优先级更高的线程

    • 线程的加入:调用Thread.join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。

    • 线程的唤醒:调用Thread.notify()方法,唤醒此对象监视器上等待的单个线程。如果想成都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。

    • 线程的同步与锁
    • 同步:是防止多个线程访问一个数据对象是时,对数据造成破坏。
    • 锁:Java每个对象都会有一个内置锁,当程序运行到非静态的synchronized同步方法上,获取当前实例的锁。 当程序运行到synchronized同步方法或者代码块时该对象锁才起作用,一个对象一个锁,当一个线程获得了该锁,只有等到该线程释放锁,其他线程才能访问,等待期间进入线程池。
    • 线程的一些问题
    • sleep()和yield()方法的区别
      • sleep()方法和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程。 两者的区别在于:sleep()方法会给其他线程运行的机会,不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。
      • 当线程执行了sleep(long millis)方法,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法,将转到就绪状态
      • sleep()方法比yield()方法具有更好的可移植性
      • sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常。 -同步的问题
      • 对于非静态字段中可更改的数据,通常使用非静态方法访问。
      • 对于静态字段中可更改的数据,通常使用静态方法访问
      • 静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
      • 对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞
    • 线程交互
      • void notify() 唤醒在此对象监视器上等待的单个线程。
      • void notifyAll() 唤醒在此对象监视器上等待的所有线程。
      • void wait() 导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法。
 1 /**
 2  * 线程锁测试
 3  *
 4  * @author
 5  * @create 2017-12-14 15:53
 6  **/
 7 public class ThreadA {
 8     public static void main(String[] args) {
 9         ThreadB b = new ThreadB();
10         ThreadC c = new ThreadC();
11         c.start();
12         b.start();
13         //线程A拥有B对象的锁,为了调用该对象wait或者notify方法,该线程必须是调用对象锁的拥有者
14             synchronized (b) {
15                 try {
16                     System.out.println(b.getName()+"开始计算");
17 //         A线程等待,当在对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁。
18                     b.wait();
19 //         此时调用notify()并没有起到唤醒的作用,没有对象锁
20                     b.notify();
21 //
22                     synchronized (c) {
23 //                        唤醒A线程
24                         c.notify();
25                     }
26                 } catch (InterruptedException e) {
27                     e.printStackTrace();
28                 }
29                 System.out.println("总值:"+b.count);
30             }
31 
32     }
33 }
 1 public class ThreadB extends Thread {
 2     int count;
 3     public void run() {
 4         synchronized (this) {
 5             for (int i = 0; i < 100; i++) {
 6               count +=i;
 7             }
 8         }
 9         System.out.println("线程B结束");
10     }
11 }
 1 public class ThreadC extends Thread {
 2     @Override
 3     public void run() {
 4         synchronized (this){
 5             try {
 6                 wait();
 7             System.out.println("c线程被唤醒。。。。");
 8             } catch (InterruptedException e) {
 9                 e.printStackTrace();
10             }
11         }
12             System.out.println("c线程的执行结束");
13 
14     }
15 }

 

  • 对象上调用wait()方法时,执行该代码的线程立即放弃它在对象上的锁,
  • 对象上调用notify()方法时,线程一定会放弃锁,如果线程仍然在执行同步代码,则线程在执行之前不会移除锁。

如有错误,欢迎指正,继续补充

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!