Thinking in Java (10)

第二十一章 并发

并行性(parallel):同一时刻,多条指令在多个处理器上同时执行

并发性(concurrency):同一时刻只有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果

进程/线程:操作系统可以同时执行多个任务,每个任务就是进程,进程可以同时执行多个任务,每个任务就是线程

一、线程的创建

1、继承Thread类

public class Thread implements Runnable {

    ...

    

}

2、实现Runnable接口

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

3、实现Callable接口

(泛型接口、有返回值、异常声明)

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

二、线程的状态转换

1、new关键字创建线程——新建状态

2、调用线程对象的start方法——就绪状态

3、就绪状态的线程获得了CPU——运行状态

4、线程调用sleep方法/阻塞式IO方法/同步方法/等待notify/调用suspend方法——阻塞状态

5、run方法/call方法执行结束/未捕获异常/stop方法——线程结束

三、线程同步

1、同步代码块

//obj同步监视器
//线程开始执行同步代码块之前,必须先获得对同步监视器的锁定
//任何时刻只能有一个线程进入同步代码块
synchronized(obj){

    ...

}

2、同步方法

同步实例方法:同步监视器为调用该方法的对象(this)

同步类方法:同步监视器为该方法所属对象的类对象

3、同步锁

Lock是控制多个线程对共享资源进行访问的工具,常用ReentrantLock、ReentrantReadWriteLock、StampedLock等

ReentrantLock(可重入锁)

ReentrantReadWriteLock(读写锁)

StampedLock(乐观读写锁)

四、线程通信

1、借助Object类提供的wait/notify/nitofyAll三个方法,这三个方法必须由同步监视器对象调用:

  • 对于使用synchronized修饰的同步方法,则该类的默认实例(this)就是同步监视器,可以在同步方法中直接调用这三个方法;
  • 对于使用synchronized修饰的同步代码块,同步监视器即为synchronized括号里的对象,则必须使用该对象调用这三个方法。

2、使用Condition控制线程通信(显式的使用Lock对象充当同步监视器):

  • Condition实例被绑定在一个Lock对象,Condition提供await/signal/signalAll三个方法
public class Test{

    // 显式定义Lock对象
    private final Lock lock = new ReentrantLock();
    // 获得指定Lock对象的Condition
    private final Condition cond = lock.newCondition();
    private boolean flag = false;

    public void m1(){
        
        lock.lock();
        try{
            if(!flag){
                cond.await();
            }else{
                ...
                cond.signalAll();
            }
        }
        catch(Exception e){}
        finally{
            lock.unlock();
        }
    }
    
    public void m2(){
        
        lock.lock();
        try{
            if(flag){
                cond.await();
            }else{
                ...
                cond.signalAll();
            }
        }
        catch(Exception e){}
        finally{
            lock.unlock();
        }
    }

}

3、阻塞队列

BlockingQueue接口的实现类:

  • ArrayBlockingQueue: 基于数组实现
  • LinkedBlockingQueue: 基于链表实现
  • PriorityBlockingQueue: 优先阻塞队列
  • SynchronousQueue: 同步队列,对该队列的存、取必须交替进行
  • DelayQueue: 集合元素必须实现Delay接口
public class Test {
    public static void main(String[] args){
        BlockingQueue<String> bq = new ArrayBlockingQueue<>(1);
        new Test().new Producer(bq).start();
        new Test().new Comsumer(bq).start();
    }

    // 生产者
    class Producer extends Thread {
        private BlockingQueue<String> producerQueue;

        private String[] elements = new String[]{"a", "b", "c", "d", "e"};

        public Producer(BlockingQueue producerQueue) {
            this.producerQueue = producerQueue;
        }

        @Override
        public void run() {
            try {
                for (int i = 0; i < 100; i++) {
                    String s = elements[i % 5];
                    producerQueue.put(s);
                    System.out.println(getName() + "生产完成:" + s);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
    // 消费者
    class Comsumer extends Thread {
        private BlockingQueue<String> comsumerQueue;

        public Comsumer(BlockingQueue comsumerQueue) {
            this.comsumerQueue = comsumerQueue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    String s = comsumerQueue.take();
                    System.out.println(getName() + "消费完成:" + s);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

五、线程池

利用池化技术提高程序性能,Executors工厂类创建线程池:

  • ExecutorService newCachedThreadPool(): 生成一个需要的时候就创建新的线程,同时可以复用之前创建的线程(如果这个线程当前没有任务)的线程池,如果线程空闲了 60 秒都没有任务,那么将关闭此线程并从线程池中移除。
  • ExecutorService newFixedThreadPool(): 生成一个固定大小的线程池
  • ExecutorService newSingleThreadPool(): 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO优先级)执行
  • ScheduledExecutorService newScheduledThreadPool(): 创建一个定长线程池,支持定时及周期性任务执行
  • ScheduledExecutorService newSingleScheduledExecutor(): 创建一个线程的线程池,支持定时及周期性任务执行
  • ExecutorService newWorkStealingPool(): 根据CPU数创建后台线程池
  • ForkJoinPool: ExecutorService的实现类,利用多核CPU,可以将任务拆分放到多个处理器核心上并行执行,是一种特殊的线程池 (ForkJoinTask代表一个可以并行、合并的任务是一个抽象类,RecursiveAction代表没有返回值的任务,RecursiveTask代表有返回值的任务)

public class Test {
    public static void main(String[] args) throws Exception {
        ForkJoinPool pool = new ForkJoinPool();
        pool.submit(new Test().new PrintTask(0, 300));
        pool.shutdown();
    }
    // 继承RecursiveAction 实现“可分解”任务
    class PrintTask extends RecursiveAction {
        private static final int NUM = 50;
        private int start;
        private int end;

        public PrintTask(int start, int end) {
            this.start = start;
            this.end = end;
        }

        @Override
        protected void compute() {
            if (end - start < NUM) {
                for (int i = start; i < end; i++) {
                    System.out.println(Thread.currentThread().getName() + " print:" + i);
                }
            }     
            else {
                // 任务拆分
                int middle = (start + end) / 2;
                PrintTask left = new PrintTask(start, middle);
                PrintTask right = new PrintTask(middle, end);
                left.fork();
                right.fork();
            }
        }
    }
}

六、其他

join方法:当在某个程序的执行流中调用其他线程的join方法时,调用线程将被阻塞,直到被join方法加入的线程执行完。

后台线程[daemon thread]:如果所有的前台线程都死亡,后台线程自动死亡。(JVM的垃圾回收线程即为后台线程)

线程执行同步方法或者同步代码块时,程序调用Thread.sleep()/Thread.yield()方法暂停当前线程执行,当前线程不会释放同步监视器。线程执行同步方法或者同步代码块时,程序执行同步监视器的wait()方法,当前线程暂停并释放同步监视器

ThreadLocal类,代表一个线程局部变量,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发导致的线程安全问题。

利用Collections的类方法将集合包装成线程安全的集合,将原集合方法包装成同步方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值