jdk1.5以后的多线程

java.util.concurrent.* java并发工具包

1. 创建线程的第三种方式

    java    10行

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;
}

与Runnable的区别,1)有返回结果,2)可以抛出检查异常

创建代码:

    java    17行

// 代表一个任务对象
// Callable代表线程中要执行的代码
FutureTask task = new FutureTask(new Callable() {
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.sleep(2000);
        return "ok";
    }
});

// 创建和启动新线程
new Thread(task).start();

// 获取返回结果
System.out.println(task.get());

2. 线程池

创建有限的线程资源为更多的任务提供服务。享元模式

通过Executors类的方法可以创建线程池。底层实现依靠一个核心的ExecutorService的实现类:ThreadPoolExecutor

    java    6行

ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)

corePoolSize 核心线程数目 (最多保留的线程数)
5
maximumPoolSize
10

workQueue 阻塞队列 如果任务超过了核心线程数,进入队列进行排队,直到有空闲的线程
10

如果任务过多,阻塞队列都放不下了,还会创建新的线程来救急
corePoolSize+救急的线程 <= maximumPoolSize(最大线程数)
21 会抛出拒绝提交任务异常

keepAliveTime 生存时间- 针对救急线程
60
unit 时间单位 秒

创建固定大小的线程池

static ExecutorService newFixedThreadPool(int nThreads)
          创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。

Executors.newFixedThreadPool(2);
核心线程数=最大线程数(没有救急线程被创建)
阻塞队列 无界,可以放任意数量的任务,
适合执行数量有限,长时间运行的任务

// java中对线程池的抽象:ExecutorService  创建一个固定大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);

/*for (int i = 0; i < 10; i++) {
    threadPool.submit(()->{
        System.out.println(Thread.currentThread().getName()+"任务执行");
    });
}*/
// 执行带有返回结果的任务
Future future = threadPool.submit(() -> {
    System.out.println(Thread.currentThread().getName()+"执行计算...");
    Thread.sleep(1000);
    return 10;
});
System.out.println(future.get());


threadPool.shutdown(); // 不接收新任务,当所有任务运行结束,整个线程池关闭

 创建缓冲线程池

static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
          创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。

Executors.newCachedThreadPool()
核心线程数是0, 最大线程数是Integer的最大值(救急线程可以无限创建)
生存时间是60s
适合任务数比较密集,但每个任务执行时间较短的情况

 创建单线程线程池

static ExecutorService newSingleThreadExecutor()
          创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

Executors.newSingleThreadExecutor()
使用场景:希望多个任务排队执行

区别:
Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改
Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改

带有日程安排功能的线程池

static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
          创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

    java    27行

ScheduledExecutorService service = Executors.newScheduledThreadPool(5);

// 让任务推迟一段时间执行
// 参数1.任务对象, 参数2,3 推迟的时间
/*service.schedule(()->{
    System.out.println("执行任务...");
}, 10L, TimeUnit.SECONDS);*/

// 以一定的频率反复执行任务(任务不会重叠)
// 参数1,任务对象, 参数2,初始推迟时间, 参数3,4 时间间隔和单位
/*service.scheduleAtFixedRate(()->{
    try {
        Thread.sleep(1200);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("hello");
}, 0, 1, TimeUnit.SECONDS);*/

// delay表示从上一个任务结束,到下一个任务开始之间的时间
service.scheduleWithFixedDelay(()->{
    System.out.println("hello");
}, 0, 1, TimeUnit.SECONDS);


//        service.shutdown();

3. 原子操作类

底层实现线程安全的类

AtomicInteger
AtomicBoolean
...

    java    24行

// 创建原子整数类
private static AtomicInteger i = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            i.getAndIncrement();  // 获取并且自增  i++
//                i.incrementAndGet();  // 自增并且获取  ++i
        }
    });

    Thread t2 = new Thread(() -> {
        for (int j = 0; j < 5000; j++) {
            i.getAndDecrement(); // 获取并且自减  i--
        }
    });

    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(i);
}

4. 线程安全集合类

StringBuffer 线程安全
String 不可变类 , 都是线程安全的
Random 线程安全
Vector 实现了List,并且线程安全
Hashtable 实现了Map,并且线程安全

5.0新增的线程安全集合类


ConcurrentSkipListMap 实现了Map(可排序),并且线程安全


CopyOnWriteArrayList 实现了List,并且线程安全

ConcurrentHashMap 实现了Map,并且线程安全

 

BlockingQueue 阻塞队列


队列 FIFO , first in first out先进先出

Queue --> LinkedList

    java    33行

private static BlockingQueue<Product> queue = new ArrayBlockingQueue<>(5);
public static void main(String[] args) {

    // 生产者线程
    new Thread(()->{
        for (int i = 0; i < 10; i++) {
            Product p = new Product(i);
            System.out.println(Thread.currentThread().getName()+"生产了:"+p);
            try {
                queue.put(p);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();

    // 消费者线程
    for (int j = 0; j < 5; j++) {
        new Thread(()->{
            for (int i = 0; i < 2; i++) {
                try {
                    Product p = queue.take();
                    System.out.println(Thread.currentThread().getName()+"消费了:"+p);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }


}

5. ThreadLocal

  多个线程使用同一变量可能会出错,我们可以让他们用自己的局部变量。

//             线程局部变量
private static ThreadLocal<SimpleDateFormat> local = new ThreadLocal() {
@Override        // 初始值
protected Object initialValue() {
    return new SimpleDateFormat("yyyy-MM-dd"); // 存入当前线程
}
};


public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
    new Thread(()->{
        try {
            SimpleDateFormat sdf = local.get(); // 获取本线程自己的局部变量
            Date date = sdf.parse("1951-10-09"); // 每个线程使用的是自己的SimpleDateFormat因此没有争用
            System.out.println(Thread.currentThread().getName() + " " + date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }).start();
}
//一下代码为出错代码
/*for (int i = 0; i < 10; i++) {
    new Thread(()->{
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = sdf.parse("1951-10-09");
            System.out.println(Thread.currentThread().getName() + " " + date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }).start();
}*/

猜你喜欢

转载自blog.csdn.net/qq_38454165/article/details/81741309