Java并发编程---基础知识

Java并发编程—基础知识

文章目录

  • Java并发编程---基础知识
    • 什么是线程
      • 线程和进程
      • 线程的状态和切换
      • 线程的创建
      • 线程的启动和执行
      • 线程的结束
      • 线程的中断
      • 守护线程(Daemon线程)
    • 线程安全和线程不安全
    • 多线程优缺点
    • 线程间通信
    • 线程相关的常用方法

什么是线程

线程是操作系统调度的最小单元,多个线程同时执行,能够提高程序的性能。

线程和进程

现代操作系统在运行一个程序时,会为其启动一个进程;在一个进程里可以创建多个线程,这些线程都拥有各自的线程私有内存:程序计数器、Java虚拟机栈、本地方法栈,且可以访问线程共享内存的共享变量。一个程序进程中多个线程的切换(或多核并行)执行,提高了程序的性能。

线程的状态和切换

Java线程在运行时的生命周期中有6种状态进行切换,在同一时刻,线程只能处于其中一个状态:

  1. NEW:新建状态,通过某种方式创建的,还未启动(调用Thread.start()方法进行启动)的线程处于该状态;
  2. RUNNABLE:运行状态,包括操作系统的 运行中和准备就绪两种状态;
  3. BLOCKED:阻塞状态,在临界区等待获取锁;
  4. WAITING:无限时等待状态,等待其他线程显示唤醒,使用Object.wait()等方法进入该状态,在该状态下不会被分配CPU时间片;
  5. TIME_WAITING:超时等待状态,同WAITING状态,不过可以在指定时间内自我唤醒,使用Object.wait(long ms)等方法进入该状态;
  6. TERMINATED:终止状态,线程已经结束执行。

线程的创建

Java中有多种方法可以实现线程的创建:

  1. 继承Thread类;
public class ThreadCreate1 extends Thread{
    private int count;

    @Override
    public void run() {
        super.run();
        System.out.println("实现了Thread的线程,这里是线程具体实现");
    }
}
  1. 实现Runable接口;
public class ThreadCreate2 implements Runnable{
    private int count;
    @Override
    public void run() {
        System.out.println("继承了Runnable的线程,这里是线程具体实现");
    }
}
  1. 实现Callable接口;
public class ThreadCreate3 implements Callable {
    private int count;

    @Override
    public String call() throws Exception {
        System.out.println("继承了Callable的线程,这里是线程具体实现");
        return "Callable";
    }
}
  1. 继承FutureTask类。
ppublic class ThreadCreate4 extends FutureTask {
    private int count;
    //演示用,实际中不会这么写
    public ThreadCreate4() {
        super(() -> {
            System.out.println("实现了FutureTask的线程,这里是线程具体实现");
            return "Callable";
        });
    }
}

演示类:

public static void main(String[] args) {
    ExecutorService executor = Executors.newFixedThreadPool(10);
    ThreadCreate1 threadCreate1 = new ThreadCreate1();
    ThreadCreate2 threadCreate2 = new ThreadCreate2();
    ThreadCreate3 threadCreate3 = new ThreadCreate3();
    ThreadCreate4 threadCreate4 = new ThreadCreate4();
  
     threadCreate1.start();
     new Thread(threadCreate2).start();
  
     Future future3 = executor.submit(threadCreate3);
     Future future4 = executor.submit(threadCreate4);
    
    }

结果:

实现了Thread的线程,这里是线程具体实现
继承了Runnable的线程,这里是线程具体实现
继承了Callable的线程,这里是线程具体实现
实现了FutureTask的线程,这里是线程具体实现

线程的启动和执行

线程类(Thread)有两个方法 :

  1. start():启动,该方法将线程加入线程组,由新建状态(NEW)转变为可执行状态(RUNABLE),当线程分配到CPU时间片时,会执行该线程的run()方法;新建线程必须调用该方法,才能被CPU执行run()方法;或者是加入线程池框架,由框架进行执行。
  2. run():执行,线程运行时方法,Thread.run()方法实际上是调用Runnable的run()方法。

线程的结束

当线程执行完成或者被中断时,会转变成终止状态(TERMINATED)。

线程的中断

Java的中断机制是一种线程协作机制,一个线程调用另一个线程的中断方法,并不能使该线程立即结束,即并不能使用中断直接终止一个线程。
每个线程对象里都有一个Boolean类型的中断标识,可以调用指定的方法来设置该标志位,线程在合适的时机通过判断中断标识位来处理中断请求,当然也可以选择不处理。

线程关于中断的方法如下:

  1. public static boolean interrupted():静态方法,检测当前线程是否已经中断,且清除中断标识位,即将Boolean值设为false;
  2. public boolean isInterrupted():检测当前线程是否已经中断,和上面的方法一样都是调用native方法private native boolean isInterrupted(boolean ClearInterrupted),只不过上面的方法传入true,本方法传入false;
  3. public void interrupt():中断线程,将中断标识位设为true的唯一方法。

响应中断的方式:

  1. 抛出InterruptedException异常,交由调用栈上层处理;
  2. 屏蔽中断请求,不做处理;
  3. 响应中断,在任务处理前判断中断标识位,提前结束任务。

何时使用中断:

  1. 点击某个桌面应用中的取消按钮时;
  2. 某个操作超过了一定的执行时间限制需要中止时;
  3. 多个线程做相同的事情,只要一个线程成功其它线程都可以取消时;
  4. 一组线程中的一个或多个出现错误导致整组都无法继续时;
  5. 当一个应用或服务需要停止时。

守护线程(Daemon线程)

守护线程是一致支持型线程,主要被用作程序中后台调度和支持下工作。
当JVM中不存在非Daemon线程时,虚拟机会退出。
通过Thread.setDaemon(boolean)方法将线程设置为守护线程。

线程安全和线程不安全

线程安全性:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么这个类就是线程安全的,否则即为线程不安全

线程的安全问题都是由全局变量或静态变量引起的,即对共享变量的访问(读或者写)会引起线程安全问题。
以下几种情况是线程安全的:

  1. 无状态的方法,即方法不访问共享变量,只使用方法栈的局部变量;
  2. 正确加锁同步的方法;
  3. 无锁同步的方法,亦作非阻塞同步。

多线程优缺点

多线程:

将任务分解成多个线程交由单核CPU分时处理,或多核CPU并行处理。

优点:

  1. 提高多核CPU的利用率:如果单线程执行,那么在一个CPU运行时,其他CPU干瞪眼不会做出一丝贡献;
  2. 提高应用处理性能;

缺点:

  1. 增加了上下文切换的开销:从任务的保存到再加载就是一次上下文切换;
  2. 线程安全性:因为多线程同时访问共享变量,如果没有做好临界区的同步,会导致结果错误,如脏数据等;
  3. 占用更多的内存空间:每个线程都需要方法栈、程序计数器等堆栈内存。

线程间通信

线程间通信是指:线程之间通过何种机制进行信息的交换。
在命令式编程中,线程间通信方式有两种:

  1. 共享内存:线程对共享内存进行读写;
  2. 消息传递

同步: 指程序中用于控制不同线程之间操作发生的相对顺序的机制。
​ - 共享内存的通信机制中,程序必须显式地指定某个方法或代码需要在线程之间进行互斥访问,即显式同步;
​ - 消息传递的通信机制中,由于消息的发送在接收之前,所以是隐式同步

Java采用共享内存的并发通信模型,整个通信过程对程序员完全透明。

线程相关的常用方法

  1. Thread.currentThread(): 获取当前正在执行的线程;
  2. Thread.sleep(long):使调用该方法的线程休眠指定的时间,继续持有原有的锁;
  3. threadA.join():调用该方法的线程等待threadA线程的结束;必须在线程启动后调用才有效,因为该方法需要判断threadA线程isActive();
    实例:
     public static void main(String[] args) {
        Thread test1 = new Thread(() -> System.out.println("test1线程执行完毕"));
        test1.start();
        Thread test2 = new Thread(() -> {
            try {
                test1.join();//线程2等待线程1执行结束
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("test2线程执行完毕");
        });
   
        test2.start();
   
        try {
            test2.join();//主线程等待线程2执行结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程执行完毕");
    
    }

结果:
test1线程执行完毕
test2线程执行完毕
main线程执行完毕

  1. Object.wait()/wait(long):使调用该方法的线程进入WAITING状态,进入该对象的等待集合,并释放该对象的锁;只有当该对象的notify()/notifyAll()方法被调用或线程被中断才会返回,从等待队列中移除;
  2. Objetc.notify():通知一个在该对象上等待的线程(任意选择,具体方式由是实现者决定),从wait()方法上返回,参与对象锁的竞争;只有竞争到了该锁才能真正返回,并该对象锁;只有持有该对象锁的线程(有且只有一个)才能调用该方法;
  3. Object.notifyAll():通知该对象上等待的所有线程进行以上操作。

你可能感兴趣的:(Java并发编程,Java并发编程总结和浅析)