Java多线程编程

多线程编程

线程和进程的概念

进程

进程是执行程序的一个执行过程,是一个动态的概念,系统资源分配的单位

程序是代码,静态概念

一个进程至少包含一个线程(主线程)

线程

线程是CPU调度和执行的单位

线程不一定调用就立即执行,由CPU调度执行

线程创建

继承Thread类

public class MyThread extends Thread(){
    @override
    public void run(){
        // 方法体
    }
    
    
   	// 线程调用
    public static void main(String args[]){
        MyThread thread = new MyThread();
        thread.start();
    }
}

实现Runnable接口

public class MyThread implements Runnable{
    @override
    public void run(){
        // 方法体
    }
    
    // 线程调用
    public static void main(String args[]){
        Thread thread = new Thread(new MyThread());
        thread.start();
        // 或者使用	匿名内部类
        new Thread(new MyThread(){
            @override
            public void run(){
                // 方法体
            }
        }).start();
        // 或者使用 Lambda 表达式 
        new Thread(()->{ // 方法体  }).start();
    }
    
}

实现Callable接口

public class MyThread implements Callable<Boolean>{
    @override
    public Boolean call(){
        // 方法体
        return true;
    }
    
    // 线程调用
    MyThread thread = new MyThread();
    // 开启服务  线程池
    ExecutorService ser = Executors.newFixedThreadPool(1);
    // 提交执行
    Future<Boolean> result = ser.submit(thread);
    // 获取结果  会抛出异常 ExecutionException InterruptedException
    boolean r1 = result.get();
    //关闭服务
    ser.shutdownNow();
}

线程状态

  1. 创建状态 new 新建一个线程
  2. 就绪状态 调用 start 方法启动线程
  3. 阻塞状态 运行时等待用户输入,线程休眠 sleep wait
  4. 运行状态 得到CPU资源时
  5. 死亡状态 线程自然执行完毕,外部干涉终止
方法 说明
setPriority(int i) 更改线程优先级
getPriotity() 获得线程优先级
currentThread().getName() 获得当前线程的名字
static void sleep(long millis) 休眠当前线程,毫秒数
void join() 等待该线程终止后再执行其他线程,其他线程阻塞 插队
static void yield() 线程礼让
void interrupt() 中断线程 尽量别用
boolean isAlive() 是否处于运行状态
stop() 终止线程
destroy() 终止线程
setDaemon(true) 开启守护线程,默认false

线程休眠

sleep方法

  • 指定当前线程阻塞的毫秒数
  • 存在 InterruptedException 异常
  • sleep时间到达后线程进入就绪状态
  • 可以模拟网络延时,倒计时等
  • 每一个对象都有一个锁,sleep不会释放锁

线程状态观测

Thread.State 线程状态 常量

  • NEW 新建未启动线程
  • RUNNABLE 在java虚拟机中执行的线程处于此状态
  • BLOCKED 被阻塞等待监视器锁定的线程处于此状态
  • WAITING 正在等待另一个线程执行特定动作的线程处于此状态
  • TIMED_WAITING 正在等待另一个线程自行动作达到指定等待时间的线程处于此状态
  • TERMINATER 已经退出的线程处于此状态

一个线程可以在给定时间点处于一个状态。这些状态是不反应任何操作系统线程状态的虚拟机状态

线程优先级

优先级范围 1~10 常量

Thread.MIN_PRIORITY =1

Thread.MAX_PRIORITY=10

Thread.NORM_PRIORITY=5

主线程优先级为 5 但是最先启动

小于 1 或大于 10 会直接出现IllegalArgumentException异常 参数异常

但是并不是优先级最高的优先执行,由CPU决定 但是多数是这样,出现这样的情况叫性能倒置

守护线程

setDaemon(true),开启守护线程

线程分为用户线程和守护线程

  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕 gc 后台记录操作日志,内存监控等

线程同步

多线程操作同一个资源

队列和锁

synchronized

当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可,可能存在以下问题

  • 一个线程尺有所会导致其他所有需要此锁的线程挂起
  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
  • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

线程不安全:

  • 没有队列会取到重复值
  • 没有锁会出现负数,临界资源问题

同步方法和同步代码块

  • 使用sychronized 修饰方法
  • 使用sychronized修饰对象或者资源

sychronized方法控制对 对象 的访问,每个对象对应一把锁,每个sychronized 方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

缺陷:若将一个大的方法申明为synchronized 将会影响效率

// 同步方法
public sychronized void method(){
    // 同步块
    synchronized(obj){}
}

synchronized(obj){}

同步块称之为同步监视器

  • obj 可以是任何对象,但是推荐使用共享资源作为同步监视器
  • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class
  • 同步监视器的执行过程
    • 第一个线程访问。锁定同步监视器,执行其中的方法
    • 第二个线程访问,发现同步监视器被锁定,无法访问
    • 第一个线程访问完毕,解锁同步监视器
    • 第二个线程访问,发现同步监视器没有锁,然后锁定并访问

JUC java.util.current包下面的线程同步类

Lock 锁

JDK5.0开始 同步锁使用Lock对象充当

java.util.concurrent.locks.Look接口是控制多个线程对共享资源进行访问的工具

锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享之前应先获得Lock对象

ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁

//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
try{
    // 加锁
lock.lock();
	// 代码
}catch(InterruptedException e){
    //建议使用try catch  但是可以不使用
}finaly{
    // 释放锁
lock.unlock()
}

死锁

某一个同步块,同时拥有两个以上对象的锁时,就可能会发生死锁问题

产生死锁的四个必要条件:

  • 互斥条件:一个资源每次只能被一个进程使用
  • 请求与保持条件:一个进程因请求资源而阻塞是,对已获得资源保持不放
  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

线程协作

生产者消费者模式

方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度

注意:均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegaMonitorStateException异常

  1. 管程法

    使用缓冲区

  2. 信号灯法

    使用标志位

线程池

经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响较大

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。避免频繁创建销毁、重复利用。类似生活中的交通工具

  • 提高了响应速度(减少创建新线程的时间)
  • 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
  • 便于线程管理
    • corePoolSize:核心池的大小
    • maximumPoolSize:最大线程数
    • keepAliveTime:线程没有任务最多保持多长时间后会终止

JDK5.0提供了线程池相关API

ExecutorService Executors

ExecutorService:真正的线程池接口,常见的自雷ThreadPoolExecutor

  • void execute(Runnable command):执行任务/命令没有返回值,一般用于执行Runnable
  • Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
  • void shutdown():关闭连接池

executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
未更新完

原文地址:https://www.cnblogs.com/linux0kk/p/13565626.html