• Java并发-线程基础


    Java并发-线程基础

    线程状态转移

    线程状态

    线程状态:

    • 新建(New)
    • 可运行(Runnable)
    • 阻塞(Blocking)
    • 无期限等待(Waiting)
    • 期限等待(Timed Waiting)
    • 死亡(Terminated)

    新建

    创建后未启动.

    可运行

    • 可能正在运行,可能在等CPU时间片.
    • 包含Running 和 Ready.

    阻塞

    等待获取排它锁.

    无期限等待

    • 等待其他线程唤醒.
    • 不分配时间片.
    进入 退出
    无参Object.wait() Object.notify()/Object.notifyAll()
    无参Thread.join() 被调用程序结束
    LockSupport.park()

    期限等待

    • 无需其他线程显式唤醒,一定时间被系统唤醒.
    • 阻塞:被动的,等待获取排它锁;等待:主动的,通过sleep()wait()进入.
    进入 退出
    Thread.sleep()方法 时间结束
    Object.wait(int time) 时间结束/Object.notify()/Object.notifyAll()
    Thread.join(int time) 时间结束/被调用线程完成
    LockSupport.parkNanos()
    LockSupport.parkUntil()

    死亡

    线程结束任务或产生异常终止.


    线程的创建

    方式:

    • 实现Runnable接口.
    • 实现Callable接口.
    • 继承Thread类.

    注:
    RunnableCallable是创建一个可以在线程中运行的任务,最终线程的创建还是通过Thread来实现.

    实现Runnable接口

    • 需实现run方法.
    • 通过Threadstart()方法启动线程.

    实现Callable接口

    • 可以有返回值.
    • 重写call()方法.
    • 返回值通过FutureTask对象进行封装.(通过Callable对象创建FutureTask对象,再通过Thread创建线程,通过FutureTask对象获得返回值)

    继承Thread类

    • 需实现run()方法.
    • 调用start()方法启动线程,JVM将线程放在就绪队列中等待调度.被调度的线程执行run()方法.

    实现接口与继承Thread

    • Java非多继承,继承Thread类就无法继承其他类,但可实现多个接口.
    • 类可能只要可执行就行,继承Thread开销大.

    基础线程机制

    Executor

    管理多个异步任务的执行,无需显式管理线程的生命周期。

    分类:

    • CachedThreadPool:一个任务创建一个线程。
    • FixedThreadPool:所有任务只能使用固定大小的线程。
    • SingleThreadExecutor:大小为1的FixedThreadPool。

    CachedThreadPool

    ExecutorService es = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {
        es.execute(()->{
            System.out. println("This is "+Thread.currentThread().getName());
        });
    }
    Thread.sleep(2000);
    es.shutdown();
    

    Daemon

    • 守护线程是程序运行时在后台提供服务的线程。
    • 当所有非守护线程结束时,程序结束,同时结束其设置的所有守护线程。
    • main()属于非守护线程。
    • 使用setDaemon()将一个线程设为守护线程。(thread.setDaemon(true))

    sleep()

    • 会休眠当前运行的线程指定的时间。
    • 可能抛出异常,需要捕获。

    yield()

    • 声明当前线程可以切换给其他线程执行。
    • 不一定让出CPU,具体由CPU决定。

    线程中断

    • 一个线程在运行期间发生异常后会提前结束。

    InterruptedException

    • 对于一个处于阻塞期限等待无期限等待状态的线程,调用其interrupt()中断线程,会抛出InterruptedException异常,从而使线程提前结束。
    • interrupt不能中断I/O阻塞或synchronized锁阻塞的线程。

    interrupted()

    • 使用interrupted()方法,在当前线程被其他线程interrupt()后,返回true,从而可以用于被中断后正常结束进程。

    Executor的中断操作

    • shutdown()方法会等待线程都执行完毕后再关闭。
    • shutdownNow()立即关闭所有线程,相当于调用所有线程的interrupt()方法。
    • 若中断指定的线程,则创建线程时使用submit()创建一个Future对象,再调用其cancel(true)进行中断。

    线程互斥同步

    两种锁机制控制多线程对共享资源的互斥访问:

    • synchronized
    • ReentrantLock

    synchronized

    同步一个代码块

    // 多线程访问同一对象时控制其互斥访问
    // 若为不同对象时,则多线程互不影响
    public void func() {
        synchronized (this) {
            // 共享变量访问代码
        }
    }
    
    // 同一对象,互斥访问
    Thread t1 = new Thread(()->{func()});
    Thread t2 = new Thread(()->{func()});
    
    // 不同对象,互不影响
    Object o1 = new Object();
    Object o2 = new Object();
    Thread t1 = new Thread(()->{o1.func()});
    Thread t2 = new Thread(()->{o2.func()});
    

    同步一个方法

    // 同一对象,多线程互斥访问
    // 不同对象,多线程互不影响
    public synchronized void func () {
        // 共享变量访问代码
    }
    

    同步一个类

    // 作用于整个类
    // 无论是否是同一个对象,同一个类的对象都互斥访问
    public void func() {
        synchronized (Test.class) {
            // 共享变量访问代码
        }
    }
    
    // 同样是作用于整个类
    public synchronized static void fun() {
        // 共享变量访问代码
    }
    
    

    ReentrantLock

    示例:

    private Lock lock = new ReentrantLock();
    
    public void func() {
        lock.lock(); // 获得锁
        try {
            // 共享变量访问代码
        } finally {
            lock.unlock(); // 释放锁,避免发生死锁。
        }
    }
    

    对比

    实现

    • synchronized是JVM实现的。
    • ReentrantLock是JDK实现的。

    性能

    大致相当。

    等待可中断

    • 持有锁的线程长期不释放锁,等待线程可以放弃等待。
    • ReentrantLock可中断,synchronized不可中断。

    公平锁

    公平锁:多个线程等待同一个锁时,按照申请的先后顺序获得锁。

    • synchronized是非公平的。
    • ReentrantLock可以是公平的,也可是非公平的。

    绑定条件

    一个ReentrantLock可以绑定多个Condition对象。


    线程间的协作

    join()

    • 当一个线程调用另外一个线程的join()方法,则当前线程挂起,直到另外线程完成后,该线程继续运行。

    wait(),notify(),notifyAll()

    • 调用wait(),使得当前线程被挂起,直到其他线程调用同一个对象实例notify()notifyAll()来唤醒挂起的线程。
    • wait()notify()notifyAll()都必须在synchronized修饰的方法内使用,否则抛出异常。
    • wait()挂起线程后,当前线程获得的锁会释放。
    • sleep()是Thread类的静态方法,并且不会释放锁。

    await(),signal(),signalAll()

    • 创建Lock对象,利用Lock对象生成Condition
    • 调用Condition上的await()方法使得线程等待,其他线程调用signal()signalAll()方法唤醒等待的线程。

    参考:

  • 相关阅读:
    C++字节对齐与位域
    使用GDB调试将符号表与程序分离后的可执行文件
    在windows上编译apr库
    使用samba共享文件夹,提供给window访问
    Linux常用命令
    使用VS2015编译xlslib库
    VS资源收藏<持续更新中>
    使用Visual Studio 2017 C++17模块(module)特性
    RMAN中format的参数
    C#的Process类的一些用法
  • 原文地址:https://www.cnblogs.com/truestoriesavici01/p/13217276.html
Copyright © 2020-2023  润新知