java多线程基础

线程状态的转换

这里写图片描述
wait()和sleep()方法的区别与相似之处:

  • wait和sleep都可以使线程进入阻塞状态。
  • wait和sleep都是可中断方法,被中断后会收到中断异常。
  • wait是Object的方法,sleep是Thread特有的方法。
  • wait方法的执行需要在同步方法中,sleep则不需要。
  • 在同步方法中执行wait方法会释放monitor锁,而sleep不会。
  • wait方法有3个重载,如果选择不指定wait时间的重载,则需要被其他线程中断后才能退出阻塞,而sleep在短暂休眠之后就会主动退出阻塞。

sleep()方法由于设置的是毫秒数,需要转换,所以建议使用TimeUnit枚举类来代替它。

yield()方法:属于一种启发式的方法,它告诉调度器我愿意放弃CPU的资源,如果CPU的资源不紧张,就会忽略这种提醒。

join()方法:在当前线程A中,创建子线程B,此时B线程调用join方法,A线程就会被一直阻塞直到被其他线程中断或等待B线程执行完毕。它也有可以设置等待时间的重载,到达这个时间就不再阻塞了。

interrupt()方法:给当前线程设置一个中断标志,可以用来打断当前状态,当线程调用上述除yield方法以外的方法时都会进入阻塞状态,而另外的线程调用被阻塞线程的interrupt方法就会打断这种阻塞,打断一个线程不代表该线程生命周期的结束,仅仅只是打断了当前的阻塞状态。

守护线程:Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出。可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程。

Hook线程:Jvm的退出是由于Jvm进程中没有活跃的非守护进程,或者收到了系统中断信号量,向虚拟机中注入一个Hook线程,在Jvm退出的时候,Hook线程会启动执行,可以通过Runtime为Jvm注入多个Hook线程。

Thread与Runnable与Callable

通常所指的线程实际上是由一个线程加上相应的执行单元。

  • Thread类
    代表线程的唯一类,创建线程只有一种方式就是构造Thread类,Thread的实例只是代表一个线程,还需要使用start方法来启动它。
    Thread类实现了Runnable接口,所以可以通过继承Thread类并重写run方法来直接添加执行单元。
  • Runnable接口
    Runnable接口只有一个run方法,实现Runnable接口并重写run方法可以创建一个执行单元,如果要启动执行单元,还需要将Runnable的实例作为构造Thread的参数。
    Runnable的run方法是没有返回值的。
  • Callable接口
    Callable接口只有一个call方法,实现Callable接口并重写call方法可以创建一个执行单元,但是它与Runnable接口不同的是,它有返回值,而Runnable没有,工具类Executors可以把一个Runnable对象包装成Callable对象,所以Callable的执行不是交由Thread,而是交给Executor框架的实现类。

Executor框架(线程池)

Executor框架有两个关键类实现了ExecutorService接口:

  1. ThreadPoolExecutor
    corePool:核心线程池的大小。
    maximumPool:最大线程池的大小。
    BlockingQueue:用来暂时保存任务的工作队列。(阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。
    支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
    支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。)
    RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler。
    这里写图片描述
    执行流程:
    这里写图片描述
    ThreadPoolExecutor还有3种类型,可以通过Executors工具类来构建:
    1).FixedThreadPool:可重用固定线程数的线程池。
    这里写图片描述
    2).SingleThreadExecutor:单个worker线程的Executor,从无限循环的队列中依次取任务执行。
    这里写图片描述
    3).CachedThreadPool:根据需要创建新线程的线程池,可能会不停创建线程,直至CPU满负荷。
    这里写图片描述
  2. ScheduledThreadPoolExecutor
    主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。

常用同步工具类

  1. 同步屏障CyclicBarrier
    CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一
    组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会
    开门,所有被屏障拦截的线程才会继续运行。
  2. 控制并发线程数的Semaphore
    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以
    保证合理的使用公共资源。
  3. 线程间交换数据的Exchanger
    Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交
    换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过
    exchange方法交换数据,如果第一个线程先执行exchange()方法,它会一直等待第二个线程也
    执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产
    出来的数据传递给对方。

实验一下:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Exchanger;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolTest {

    public static void main(String[] args) {
        //Executors.newFixedThreadPool(10);

        //线程池的创建
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 20, 5000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

        //用于线程之间交换数据
        Exchanger<String> exchanger = new Exchanger<>();

        //用于控制线程的并发数量,设置通行证的数量,此处是2
        //new Semaphore(permits);//permits:通行证数量
        //Semaphore semaphore = new Semaphore(2);
        //下3个方法在线程中使用
        //semaphore.acquire();//表示当前线程获取通行证
        //semaphore.tryAcquire(timeout, unit);//表示当前线程尝试获取通行证,timeout为设置的时长,后面为单位(通常为TimeUnit类)
        //semaphore.release();//表示当前线程释放通行证

        //用于拦截线程,当线程的数量达到一定数量之后就会放行,此处是2
        //new CyclicBarrier(parties);//parties拦截的总数量,达到后就会放行
        //new CyclicBarrier(parties, barrierAction);//barrierAction:在放行后具有优先执行的权力的线程或执行单元
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2);//当调到3后,2个子线程都不会打印信息,因为永远等不到第三个线程来

        threadPoolExecutor.execute(new Runnable() {

            @Override
            public void run() {
                String a = "hello, I am a-Thead!";
                try {
                    //该线程调用之后就会判断是否已经满足屏障放行要求,不满足需要继续等待
                    cyclicBarrier.await();
                    System.out.println(a);
                    //将数据a放入交换器
                    exchanger.exchange(a);
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
        threadPoolExecutor.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    //该线程调用之后就会判断是否已经满足屏障放行要求,不满足需要继续等待
                    cyclicBarrier.await();
                    //从交换器中取出名称为a的数据
                    String a = exchanger.exchange("a");
                    String b = "hello, I am b-Thread!";
                    System.out.println(b + " I get the sentences: " + a);
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
        threadPoolExecutor.shutdown();
        System.out.println("the end!");
    }

}

参考书籍:java并发编程的艺术,java高并发编程详解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值