多线程知识点总结(第一篇)。。。


【说明:网络课程笔记。】

多线程知识点总结

(1)实现Runnable接口、或者继承Thread类;重写里面的run方法。

public class ThreadDemo01 {
   public static void main(String[] args) {
       System.out.println("main start...");
       Thread t=new Thread(){
           @Override
           public void run() {
               System.out.println("thread run...");
               System.out.println("thread end.");
           }
       };
       t.start();
       System.out.println("main end...");
   }
}

(2)join用于等待被调用线程执行结束后,再执行调用线程下面的代码。

public class JoinDemo02 {
   public static void main(String[] args) throws InterruptedException {
       List<Thread> threads=new ArrayList<>();
       for (String name : Arrays.asList("Bob","Alice","Tom")){
           threads.add(new HelloThread2(name));
       }
       System.out.println("main start...");
       for (Thread t : threads) {
           t.start();
           //主线程调用t线程的join.强制让一个线程执行完,再进行器后续
           //的操作,有强制串行化的效果。
//            t.join();
       }
       //修改后的执行效果,让主线程在子线程执行结束后,才能执行结束代码
       for (Thread t:threads){
           t.join();
       }
       System.out.println("main end.");
   }
}
class HelloThread2 extends Thread{
  String name;

   public HelloThread2(String name) {
       this.name = name;
   }

   @Override
   public void run() {
       System.out.println("Hello, "+name+"!");
       try {
           sleep(3000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("Goodbye,"+name+"!");
   }
}

(3)interrupted中断,中断线程检测isInterrupted标志, 如果线程处于等待状态,则会捕获InterruptedException异常。

public class InterruptDemo {
   public static void main(String[] args) throws InterruptedException {
       Thread t=new HelloThread3();
       t.start();
       //主线程等待1秒
       Thread.sleep(1000);
       //主线程发起中断t线程通知。
       //主线程通过调用t线程的interrupt方法中断t线程。
       t.interrupt();
   }
}

class HelloThread3 extends Thread {
   @Override
   public void run() {
       //中断线程需要通过检测isInterrupted标志
       while (!isInterrupted()) {
           System.out.println("Hello");
           /**
            * 如果线程处于等待状态,该线程会捕获InterruptedException异常。
            */
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
               /**
                * 捕获到InterruptedException说明有其他线程对其调用
                * 了interrupt()方法,通常情况下线程应该立刻结束运行。
                */
               return;
           }
       }
   }
}

(4)守护线程, t.setDaemon(true)。

public class DaemonThreadDemo {
   public static void main(String[] args) throws InterruptedException {
       System.out.println("main start...");
       TimerThread t=new TimerThread();
       //设置t线程为守护线程,当主线程结束后,守护线程也跟随结束线程。
       /**
        * 守护线程是为其他线程服务的线程
        * 所有非守护线程都执行完毕后,虚拟机退出
        * 守护线程不能持有资源(如打开文件等)
        */

       t.setDaemon(true);
       t.start();
       Thread.sleep(5000);
       System.out.println("main end.");
   }
}
class TimerThread extends Thread{
   @Override
   public void run() {
       while (true){
           System.out.println(LocalTime.now().format(DateTimeFormatter.ofPattern("HH::mm::ss")));
           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
   }
}

(5)synchronized的概念,以及要解决的问题:多线程竞争的问题。但没有解决线程之间相互协调的问题。

public class SyncDemo01 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new AddThread();
        Thread t2=new DecThread();
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(Counter.count);
    }
    public static class Counter{
        //锁对象的典型声明。
        public static final Object LOCK=new Object();
        final static int LOOP=100000;
        public static int count=0;


    }
    public static class AddThread extends Thread{
        @Override
        public void run() {
            for (int i=0;i<Counter.LOOP;i++){
              /*  //未加锁的时候,结果不为0
                Counter.count+=1;*/


                //加锁后的最终结果为0
                synchronized (Counter.class){
                    Counter.count+=1;
//                    System.out.println("加1后,结果为:"+Counter.count);
                }

            }
        }
    }
    public static class DecThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < Counter.LOOP; i++) {
               /* //未加锁的时候,结果不为0
                Counter.count+=1;*/
                //加锁后的最终结果为0
                synchronized (Counter.class){
                    Counter.count-=1;
//                    System.out.println("减1后,结果为:"+Counter.count);
                }

            }
        }
    }
}

(6)synchronized----wait----notify/notifyAll。解决线程之间相互协调的问题。

public class WaitNotifyDemo01 {
   public static void main(String[] args) throws InterruptedException {
       TaskQueue taskQueue = new TaskQueue();
       WorkerThread worker = new WorkerThread(taskQueue);
       worker.start();
       //add task:
       taskQueue.addTask("Bob");
       Thread.sleep(1000);
       taskQueue.addTask("Alice");
       Thread.sleep(1000);
       taskQueue.addTask("Tim");
       Thread.sleep(1000);
       worker.interrupt();
       worker.join();
       System.out.println("End");
   }
}

class TaskQueue {
   //定义一个任务队列,用于后面的添加任务和获取任务。
   final Queue<String> queue = new LinkedList<>();

   //添加任务方法,
   public synchronized void addTask(String s) {
       this.queue.add(s);
       //notify通知之前处于wait状态的线程,让其进入唤醒的状态。
       //此时被锁定的方法,还持有该锁,待该锁释放后。之前被唤醒的方法,可以竞争到该锁。
       /*this.notify();*/
       this.notifyAll();
   }

   public synchronized String getTask() throws InterruptedException {
       while (queue.isEmpty()) {
           //wait会释放this锁,这点很重要。
           //如果队列里面没有元素,需要进入等待状态,并释放锁。
           //待另一个线程往队列里面添加元素后,对其进行唤醒。
           System.out.println("线程开始进行等待状态。。。");
           this.wait();
       }
       return queue.remove();
   }
}

class WorkerThread extends Thread {
   TaskQueue taskQueue;

   public WorkerThread(TaskQueue taskQueue) {
       this.taskQueue = taskQueue;
   }

   @Override
   public void run() {
       while (!isInterrupted()) {
           String name;
           try {
               name = taskQueue.getTask();
           } catch (InterruptedException e) {
               /*e.printStackTrace();*/
               System.out.println("线程被主线程中断,退出当前线程中。。。");
               return;
           }
           String result = "Hello, " + name + "!";
           System.out.println(result);
       }
   }
}

(7)异常处理的原则:工具类只需抛出异常,谁调用谁负责,谁处理。

在以下片段中,并没有处理异常,而是选择抛出InterruptedException的异常。

class TaskQueue {
   //定义一个任务队列,用于后面的添加任务和获取任务。
   final Queue<String> queue = new LinkedList<>();

   //添加任务方法,
   public synchronized void addTask(String s) {
       this.queue.add(s);
       //notify通知之前处于wait状态的线程,让其进入唤醒的状态。
       //此时被锁定的方法,还持有该锁,待该锁释放后。之前被唤醒的方法,可以竞争到该锁。
       /*this.notify();*/
       this.notifyAll();
   }

   public synchronized String getTask() throws InterruptedException {
       while (queue.isEmpty()) {
           //wait会释放this锁,这点很重要。
           //如果队列里面没有元素,需要进入等待状态,并释放锁。
           //待另一个线程往队列里面添加元素后,对其进行唤醒。
           System.out.println("线程开始进行等待状态。。。");
           
           /*特别注意,此处选择抛出异常,而不是处理异常。*/
           /*特别注意,此处选择抛出异常,而不是处理异常。*/
           /*特别注意,此处选择抛出异常,而不是处理异常。*/
           this.wait();
       }
       return queue.remove();
   }
}

(8)ReentrantLock

ReentrantLock可以替换synchronized
ReentrantLock获取锁更安全
ReentrantLock是类,需要创建一个锁对象lock。在需要加锁的地方加锁,调用lock.lock();。在finally块中,调用lock.unlock释放锁。

/*
lock.lock();
使用
try{}finally{lock.unlock()}
来保证锁能够正常的释放。
*/
/**
*ReentrantLock:
* 可重入锁,一个线程可多次获取同一个锁。
* lock()方法可获取锁。
* tryLock()方法可尝试获取锁并可指定超时。不用一直等到下去。。
*/

import sun.java2d.pipe.LoopBasedPipe;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo1 {
   public static int LOOP=100000;

   public static void main(String[] args) throws InterruptedException {
       Counter counter=new Counter();
       Thread t1=new Thread(){
           @Override
           public void run() {
               for (int i=0;i<LOOP;i++) {
                   counter.add(1);
               }
           }
       };
       Thread t2=new Thread(){
           @Override
           public void run() {
               for (int i=0;i<LOOP;i++) {
                   counter.dec(1);
               }
           }
       };
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println(counter.getValue());
   }
}


class Counter{
   //新建一个ReentrantLock实例,向上造型为Lock接口。
   private Lock lock=new ReentrantLock();
   private int value=0;
   public void add(int m){
       //获得当前对象的锁。必须在try之外完成。
       lock.lock();
       try {
           this.value+=m;

       } finally {
           //释放锁。必须在finally里面完成。
           lock.unlock();
       }

   }
   public void dec(int m){
       //在try之外加锁
       lock.lock();
       try {
           this.value-=m;
       } finally {
           //在finally里面释放锁。
           lock.unlock();
       }

   }

   public int getValue() {
       return value;
   }
}

class Counter2{
   final Lock lock=new ReentrantLock();
   public void inc() throws InterruptedException {
       if (lock.tryLock(1, TimeUnit.SECONDS)){
           try {
               //需同步代码块。
           } finally {
               lock.unlock();
           }
       }
   }
}
/**
* 总结
* ReentrantLock可以替代synchronized
* ReentrantLock获取锁更安全
* 必须使用try...finally保证正确获取和释放锁
*/


(9)ReadWriteLock

允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待。
使用ReadWriteLock可以解决:
值允许一个线程写入(其他线程既不能写入也不能读取)
没有写入时,多个线程允许同时读(提高性能)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* 允许多个线程同时读,但只要有一个线程在写,其他线程就必须等待。
* 使用ReadWriteLock可以解决:
* 只允许一个线程写入(其他线程既不能写入也不能读取)
* 没有写入时,多个线程允许同时读(提高性能)
*/

public class ReadWriteLockDemo01 {
   final static int LOOP=10000000;

   public static void main(String[] args) throws InterruptedException {
       Counter counter=new Counter();
       Thread t1=new Thread(){
           @Override
           public void run() {
               for (int i = 0; i < LOOP; i++) {
                   counter.add(1);
               }
           }
       };
       Thread t2=new Thread(){
           @Override
           public void run() {
               for (int i = 0; i < LOOP; i++) {
                   counter.dec(1);
               }
           }
       };
       t1.start();
       t2.start();
       t1.join();
       t2.join();
       System.out.println(counter.get());
       System.out.println("main end.");
   }

}


class Counter{
   private static int value=0;
   final ReadWriteLock lock=new ReentrantReadWriteLock();
   final Lock rLock=lock.readLock();
   final Lock wLock=lock.writeLock();
   public void add(int m){
       wLock.lock();
       try {
           value+=m;
       } finally {
           wLock.unlock();
       }
   }
   public  void dec(int m){
       wLock.lock();
       try {
           value-=m;
       } finally {
           wLock.unlock();
       }
   }
   public int get(){
       rLock.lock();
       try {
           return value;
       } finally {
           rLock.unlock();
       }
   }
}
/**
* ReadWriteLock适用条件:
* 同一个实例,有大量线程读取,仅有少量线程修改。
* 总结
* 使用ReadWriteLock可以提高读取效率:
* ReadWriteLock只允许一个线程写入
* ReadWriteLock允许多个线程同时读取
* ReadWriteLock适合读多写少的场景
*/

(10)Condition

Condition是一个类,使用的时候,需要创建一个condition对象,需要用ReentrantLock的对象来创建condition对象。
condition对象的await和signal和wait、notify的使用方式及效果一致。

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo {

   public static void main(String[] args) throws InterruptedException {
       TaskQueque taskQueque=new TaskQueque();
       WorkerThread worker=new WorkerThread(taskQueque);
       worker.start();
       //添加新任务:
       taskQueque.addTask("刘备");
       Thread.sleep(1000);
       taskQueque.addTask("张飞");
       Thread.sleep(1000);
       taskQueque.addTask("关羽");
       Thread.sleep(1000);
       worker.interrupt();
       worker.join();
       System.out.println("main end.");
   }
}
class TaskQueque{
   //定义一个任务队列,用于后面的添加任务和获取任务。
   final Queue<String> queue = new LinkedList<>();
   final Lock lock =new ReentrantLock();
   final Condition condition=lock.newCondition();
   public String getTask() throws InterruptedException {
       lock.lock();
       try {
           while (this.queue.isEmpty()) {
               //相当于wait---notify里面的wait.
               condition.await();
           }
           return queue.remove();
       }  finally {
           lock.unlock();
       }
   }
   public void addTask(String name){
       lock.lock();
       try {
           this.queue.add(name);
           //相当于wait--notifyAll中调用notifyAll方法。
           condition.signalAll();
       } finally {
           lock.unlock();
       }
   }
}
class  WorkerThread extends Thread{
   TaskQueque taskQueque;

   public WorkerThread(TaskQueque taskQueque) {
       this.taskQueque = taskQueque;
   }

   @Override
   public void run() {
       String name;
       while (!isInterrupted()){
           try {
               name=taskQueque.getTask();
           } catch (InterruptedException e) {
               System.out.println("线程被中断线程中断中。。。");
               break;
           }
           String result="Hello, "+name+"!";
           System.out.println(result);
       }


   }
}
/**
* Condition
* Condition.await --Condition.signal--signalAll原理和
* wait  -------------notify--------------notifyAll一致
* await()会释放当前锁,进入等待状态
* signal()会唤醒某个等待线程
* signalAll()会唤醒所有等待线程
* 唤醒线程从await()返回后需要重新获得锁
*
*/
/**
* 总结:
* Condition对的await---signalAll可以替代wait  - notifyAll。
* Condition对象必须从ReentrantLock对象获取
* ReentrantLock+Condition可以替代synchronized+wait/notify
*/

(11)Concurrent集合

java.util.concurrent提供了线程安全的Blocking集合
ArrayBlockingQueue

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/*
多线程
java.util.concurrent提供了线程安全的Blocking集合
ArrayBlockingQueue

 */
public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> taskQueue=new ArrayBlockingQueue<>(100);
        WorkerThread worker=new WorkerThread(taskQueue);
        worker.start();
        //add task:
        taskQueue.put("Bob");
        Thread.sleep(1000);
        taskQueue.put("Alice");
        Thread.sleep(1000);
        taskQueue.put("Tim");
        Thread.sleep(1000);
        worker.interrupt();
        worker.join();
        System.out.println("main end.");
    }
}

class WorkerThread extends Thread{
    BlockingQueue<String> taskQueue;

    public WorkerThread(BlockingQueue<String> taskQueue) {
        this.taskQueue = taskQueue;
    }

    @Override
    public void run() {
        while (!isInterrupted()){
            String name;
            try {
                name=taskQueue.take();
            } catch (InterruptedException e) {
                System.out.println("收到主线程中断信号,中断中。。。");
                break;
            }
            String result="Hello, "+name+"!";
            System.out.println(result);
        }
    }
}
/*
java.util.concurrent提供了线程安全的Blocking集合:
Interface     Non-thread safe       Thread safe
List          ArrayList             CopyOnWriteArrayList
Map           HashMap               ConcurrentHashMap
Set           HashSet,TreeSet       CopyOnWriteArraySet
Queue         ArrayDeque,LinkedList ArrayBlockingQueue,LinkedBlockingQueue
Deque         ArrayDeque,LinkedList LinkedBlockingDeque
-----------------------------------------
//不推荐使用。。
Collections
java.util.Collections工具类还提供了旧的线程安全集合转换器
Map   unsafeMap = new HashMap();
Map   threadSafeMap = Collections.synchronizedMap(unsafeMap);
-----------------------------------------
总结
使用java.util.concurrent提供的Blocking集合可以简化多线程编程
多线程同时访问Blocking集合是安全的
尽量使用JDK提供的concurrent集合,避免自己编写同步代码
 */

(12)Atomic原子类

java.util.concurrent.atomic提供了一组原子类型操作

/*
Atomic
java.util.concurrent.atomic提供了一组原子类型操作
AtomicInteger
int addAddGet(int delta)
int incrementAndGet()
int get()
int compareAndSet(int expect,int update)
------------------------------------------
Atomic 类可以实现:
无锁(lock-free)实现的线程安全(thread-safe)访问

 */

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicDemo {
    final static int LOOP=1000000;
    public static void main(String[] args) throws InterruptedException {
        Counter counter=new Counter();
        Thread t1=new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < LOOP; i++) {
                    counter.add(1);
                }
            }
        };
        Thread t2=new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < LOOP; i++) {
                    counter.dec(1);
                }
            }
        };
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.get());
        System.out.println("main end.");
    }
}

class Counter{
    private AtomicInteger value=new AtomicInteger(0);
    public int add(int m){
        return this.value.addAndGet(m);
    }
    public int dec(int m){
        return this.value.addAndGet(-m);
    }
    public int get(){
        return this.value.get();
    }
}
/*
总结
使用java.util.atomic提供的原子操作可以简化多线程编程:
AtomicInteger/AtomicLong/AtomicIntegerArray等
原子操作实现了无锁的线程安全
适用于计数器,累加器等。
 */

(13)ExcutorServices

/*
ExecutorService----线程池服务
JDK提供了ExecutorService接口表示线程池
---------------------------------------------
多线程
Java语言内置多线程支持
创建线程需要操作系统资源(线程资源,栈空间。。。)
频繁创建和销毁线程需要消耗大量时间
---------------------------------------------
线程池:
线程池维护若干个线程,处于等待状态
如果有新任务,就分配一个空闲线程执行
如果所有线程都处于忙碌状态,新任务放入队列等待。
---------------------------------------------
常用ExecutorService:
FixedThreadPool:线程数固定的线程池。
CachedThreadPool:线程数根据任务动态调整的线程池
SingleThreadExecutor:仅单线程执行
--------------------------------------------
 */


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceDemo {
    public static void main(String[] args) {
       /*
                                                固定大小的线程池
        ExecutorService executor= Executors.newFixedThreadPool(4);
        executor.submit(task1);
        executor.submit(task2);
        executor.submit(task3);
        */



    }
}

import java.util.concurrent.*;


public class ThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
       /*//固定大小的线程池,比如,固定为3个线程
        ExecutorService executor= Executors.newFixedThreadPool(3);*/
       /* //单个线程的线程池
        ExecutorService executor= Executors.newSingleThreadExecutor();*/
        /*//根据任务数动态确定线程数量的线程池,但不能确定其上限
        ExecutorService executor= Executors.newCachedThreadPool();*/
        //浏览newCachedThreadPool()源代码,得到可以确定动态的线程池,并确定其上限的线程池。
        //但该方法在测试后,发现限制线程上限,实际执行过程中,偶尔会引发异常。
     /*   ExecutorService executor=new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());*/
         ExecutorService executor=new ThreadPoolExecutor(0, 10,
                60L, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>());



        executor.submit(new PrintTask("Bob"));
        executor.submit(new PrintTask("Alice"));
        executor.submit(new PrintTask("Tim"));
        executor.submit(new PrintTask("Robot"));
        Thread.sleep(10000);
        executor.shutdown();
    }

}
class PrintTask implements Runnable{
    String name;

    public PrintTask(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("Hello, "+name+"!");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/*
ScheduledThreadPool:
一个任务可以定期反复执行
每隔多长时间开始一个新的重复任务。每隔5秒钟执行
AtFixedRate
待任务执行完成后,间隔多长时间开始一个重复的新任务。间隔5秒钟执行
WithFixedDelay
-----------------------------------------------------------
问题:
FixedRate模式下,如果任务执行时间过长,后续任务会不会并发执行?

如果任务抛出了异常,后续任务是否继续执行?
----------------------------------------------------------
被取代的类。。。
java.util.Timer
一个Timer对应一个Thread
必须在主线程结束时调用Timer.cancel()
----------------------------------------------------------
总结:
JDK提供了ExecutorService实现了线程池功能
线程池内部维护一组线程,可以高效执行大量小任务
Executors提供了静态方法创建不同类型的ExecutorService
必须调用shutdown()关闭ExecutorService
ScheduledThreadPool可以定期调度多个任务。

 */
public class ScheduleDemo {
    public static void main(String[] args) {
        ScheduledExecutorService executor= Executors.newScheduledThreadPool(3);
        executor.scheduleAtFixedRate(new HelloTask("Bob"),2,5, TimeUnit.SECONDS);
        executor.scheduleAtFixedRate(new HelloTask("哈哈"),2,5, TimeUnit.SECONDS);
        executor.scheduleWithFixedDelay(new HelloTask("Alice"),2,5,TimeUnit.SECONDS);


    }
}

class HelloTask implements Runnable{
    String name;

    public HelloTask(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("Hello, "+name+"!It is "+ LocalTime.now());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Goodbye, "+name+"It is "+LocalTime.now());

    }
}

(14)Future接口

Future接口:
表示一个未来可能会返回的结果。

/*
Callable<T>接口
class Task implements Callable<String>{
        public String call() throws Exception{
             return longTimeCalculation();
        }
}
----------------------------------------------
相比于Runnable(需要重写run方法)接口,提供了一个返回值,可以获取线程
运行后的一个结果。Callable<T>需要重写call方法。
----------------------------------------------
如何获得异步执行的结果呢?
Callable<String> task=new Task();
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<String> future = executor.submit(task);
//可能阻塞
String result=future.get();
----------------------------------------------
Future<V>接口:
表示一个未来可能会返回的结果
get()
get(long timeout,TimeUnit unit)
cancel(boolean mayInterruptIfRunning)
isDone();
-----------------------------------------------
Runnable     vs      Callable
不需要返回结果        需要返回结果
-----------------------------------------------
提交Callable任务,可以获得一个Future对象
可以用Future在将来某个时刻获得结果
 */


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.*;

public class FutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor= Executors.newFixedThreadPool(3);
        DownloadTask task=new DownloadTask("http://news.baidu.com/");
        Future<String> future=executor.submit(task);
        String html=future.get();
        System.out.println(html);
        executor.shutdown();
    }
}
class DownloadTask implements Callable<String>{
    String url;

    public DownloadTask(String url) {
        this.url = url;
    }

    @Override
    public String call() throws Exception {
        System.out.println("Start download "+url+"...");
        URLConnection conn=new URL(this.url).openConnection();
        conn.connect();
        try(BufferedReader reader=new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"))){
            String s=null;
            StringBuilder sb=new StringBuilder();
            while ((s=reader.readLine())!=null){
                sb.append(s).append("\n");
            }
            return sb.toString();
        }
    }
}

(15)CompletableFuture

import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;

/*
使用Future获得异步执行结果:
Future<String> future = executor.submit(task);
String result=future.get();//可能阻塞
while(!future.isDone()){
     Thread.sleep(1);
}
String result = future.get();//可能阻塞
----------------------------------------------
使用Future获得异步执行结果:
阻塞方法:get()
轮询:isDone()
----------------------------------------------
CompletableFuture接口:
里面通过回调函数来获得结果。
CompletableFuture<String> cf=getCompletableFutureFromSomewhere();
cf.thenAccept(result->{sout("正常运行获得异步结果:"+result)});
cf.exceptionally(t->{sout("运行发生异常:"+t.getMassage())}

 */
public class CompletableFutureDemo {
    public static void main(String[] args) {
        CompletableFuture<Float>
                getStockFuture=CompletableFuture.supplyAsync(new StockSupplier());
        getStockFuture.thenAccept(x->{
            System.out.println("Current price:"+x);
        });
        getStockFuture.exceptionally(e->{
            System.out.println("Error:"+e.getMessage());
            return Float.NaN;
        });
        getStockFuture.join();
    }
}
class StockSupplier implements Supplier<Float>{
    @Override
    public Float get() {
        String url="http://hq.sinajs.cn/list=sh000001";
        System.out.println("GET:"+url);
        try {
            String result=DownloadUtil.download(url);
            String[] ss=result.split(",");
            return Float.parseFloat(ss[3]);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();
        }
    }
}


/*
CompletableFuture的优点:
异步任务结束时,会自动回调某个对象的方法
异步任务出错时,会自动回调某个对象的方法
主线程设置好回调后,不再关心异步任务的执行
---------------------------------------
CompletableFuture基本用法:
CompletableFuture<String> cf=CompletableFuture.supplyAsync(异步执行实例);
cf.thenAccept(获得结果后的操作);
cf.exceptionally(发生异常时的操作);
-------------------------------------------------------------
多个CompletableFuture可以串行执行:
CompletableFuture<String> cf1=CompletableFuture.supplyAsync(异步执行实例1);
CompletableFuture<LocalDate> cf2=cf1.thenApplyAsync(异步执行实例2);
CompletableFuture<Float> cf3=cf2.thenApplyAsync(异步执行实例3);
cf3.thenAccept(实例3获得结果后的操作);
-----------------------------------------------
CompletableFuture的命名规则:
xxx():继续在已有的线程中执行
xxxAsync():用Executor的新线程执行
-----------------------------------------------
总结
CompletableFuture对象可以指定异步处理流程:
。thenAccept()处理正常结果
。exceptional()处理异常结果
。thenApplyAsync() 用于串行化另一个CompletableFuture
。anyOf/allOf用于并行化两个CompletableFuture
 */
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.Callable;


public class DownloadUtil implements Callable<String>   {
    String url;

    public DownloadUtil(String url) {
        this.url = url;
    }

    @Override
    public String call() throws Exception {
        System.out.println("Start download "+url+"...");
        URLConnection conn =new URL(this.url).openConnection();
        conn.connect();
        try(BufferedReader reader=new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF-8"))){
            String s=null;
            StringBuilder sb=new StringBuilder();
            while ((s=reader.readLine())!=null){
                sb.append(s).append("\n");
            }
            return sb.toString();
        }
    }
    public static String download(String url) throws Exception {

        DownloadUtil dlu=new DownloadUtil(url);
        return dlu.call();
    }
}

(16)Fork/Join

/*
Fork/Join线程池可以执行一种特殊的任务:
把一个大任务拆成多个小任务并行执行
JDK1.7引入
 */
public class ForkJoinDemo {
    public static void main(String[] args) {
        long[] array=new long[100000000];
        long expectedSum=0;
        for (int i = 0; i < array.length; i++) {
            array[i]= (long) (Math.random()*100);
        }
        System.out.println("Expected sum:"+expectedSum);
        //fork-join
        ForkJoinTask<Long> task=new Sumtask(array,0,array.length);
        long startTime=System.currentTimeMillis();
        Long result= ForkJoinPool.commonPool().invoke(task);
        long endtime=System.currentTimeMillis();
        System.out.println("fork-join sum:"+result+" in "+(endtime-startTime));
    }

}

class Sumtask extends RecursiveTask<Long> {
    static final int THRESHOLD = 5000;
    long[] array;
    int start;
    int end;

    public Sumtask(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }


    @Override
    protected Long compute() {
        if (end - start <= THRESHOLD) {
            //如果任务足够小,直接计算;
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += this.array[i];
//                try {
//                    Thread.sleep(2);
//                } catch (InterruptedException e) {
//
//                }
            }
            return sum;
        }
        //任务太大,一分为二;
        int middle = (end + start) / 2;
        Sumtask subtask1=new Sumtask(this.array,start,middle);
        Sumtask subtask2=new Sumtask(this.array,middle,end);
        invokeAll(subtask1,subtask2);
        Long subresult1=subtask1.join();
        Long subresult2=subtask2.join();
        Long result=subresult1+subresult2;
        System.out.println("result="+subresult1+"+"+subresult2);
        return result;
    }
}
/*
Fork-Join是一种基于“分治”的算法。
分解任务+合并结果
ForkJoinPool线程池可以把一个大任务分拆成小任务并行执行
任务类必须继承自RecursiveTask-RecursiveAction
使用Fork-Join模式可以进行并行计算提高效率
 */

猜你喜欢

转载自blog.csdn.net/fanjianglin/article/details/102639156