线程池-4

一、什么是线程池?

线程池就是在线程使用之前先把线程创建出来,把线程放在一个池子里面养着,等需要用的时候直接使用,任务完成之后再把线程归还。

二、线程池有哪些作用

第一:提供响应效率,有了线程池,当有任务的时候,就有线程直接去完成任务,不用调用系统的资源去新建线程

第二:节约系统资源,线程池中的线程是可以回收重复利用的,降低新建和销毁线程的资源消耗。

第三:提高线程的管理性,我们知道线程是稀缺资源,不能一直无限制的创建,通过使用线程池我们可以一分配、调优

三、线程池的创建方式

Java是天生就支持并发的语言,支持并发意味着多线程,线程的频繁创建在高并发及大数据量是非常消耗资源的,因为java提供了线程池。在jdk1.5以前的版本中,线程池的使用是及其简陋的,但是在JDK1.5后,有了很大的改善。JDK1.5之后加入了java.util.concurrent包,java.util.concurrent包的加入给予开发人员开发并发程序以及解决并发问题很大的帮助。这篇文章主要介绍下并发包下的Executor接口,Executor接口虽然作为一个非常旧的接口(JDK1.5 2004年发布),但是很多程序员对于其中的一些原理还是不熟悉,因此写这篇文章来介绍下Executor接口,同时巩固下自己的知识。如果文章中有出现错误,欢迎大家指出。

Executor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newScheduledThreadPool、newFixedThreadPool、newCachedThreadPool方法其实也只是ThreadPoolExecutor的构造函数参数不同而已。通过传入不同的参数,就可以构造出适用于不同应用场景下的线程池,那么它的底层原理是怎样实现的呢,这篇就来介绍下ThreadPoolExecutor线程池的运行过程。

 

corePoolSize: 核心池的大小。 当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程;
keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。
unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

线程池四种创建方式

Java通过Executors(jdk1.5并发包)提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

代码演示:

创建可缓存的线程池:

package com.junlaninfo.CachedThreadPool;

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

/**
 * Created by 辉 on 2020/5/24.
 * 可缓存线程池
 */
public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i=0;i<100;i++){
            executorService.execute(new Runnable() {
                public void run() {
                    //打印出来线程的名字是重复的
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

创建定长线程池

package com.junlaninfo.FixedThreadPool;

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

/**
 * Created by 辉 on 2020/5/24.
 * 创建一个定长线程池
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()
 */
public class FixedThreadPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int i=0;i<10;i++){
            executorService.execute(new Runnable() {
                public void run() {
                    //通过打印线程的名字我们可以知道,只有三个线程
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

 

创建定时执行的线程池

package com.junlaninfo.ScheduledThreadPool;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by 辉 on 2020/5/24.
 * 定时执行的线程池
 */
public class ScheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        for (int i=0;i<10;i++){
            scheduledExecutorService.schedule(new Runnable() {
                public void run() {
                 System.out.println("当前线程是:"+Thread.currentThread().getName());
                }
            },3, TimeUnit.SECONDS);

        }
    }
}

创建单一线程的线程池,遵循先进先出的原则,不会造成线程安全的问题

package com.junlaninfo.SingleThreadExecutor;

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

/**
 * Created by 辉 on 2020/5/24.
 * 创建一个单线程的线程池,可以保证执行顺序
 */
public class SingleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            public void run() {
                for (int i=0;i<10;i++){
                    System.out.println(Thread.currentThread().getName()+"数字是:"+i);
                }
            }
        });


    }
}

四、重入锁

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利

重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该代码的锁,但不受影响。
在JAVA环境下 ReentrantLock 和synchronized 都是可重入锁

 

代码演示synchronized的可重入性

package com.junlaninfo.Reentrantlock;



/**
 * Created by 辉 on 2020/5/24.
 * 演示synchronized的可重入锁
 * set方法和get方法需要this,这把锁,当访问get方法的时候,获取到this锁,同时get方法内访问set方法,
 * 这时候也可以拿到this这把锁,这就是锁的可重入
 */
public class synchronizedtest implements Runnable {
    public synchronized void set() {
        System.out.println("我是set方法");

    }

    public synchronized void get() {
        set();
        System.out.println("我是get方法");
    }

    public void run() {
        get();
    }

    public static void main(String[] args) {
        synchronizedtest synchronizedtest = new synchronizedtest();
        new Thread(synchronizedtest).start();
        new Thread(synchronizedtest).start();
        new Thread(synchronizedtest).start();
    }
}

代码演示lock锁的可重入性

package com.junlaninfo.Reentrantlock;

import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.xerces.internal.dom.PSVIAttrNSImpl;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by 辉 on 2020/5/24.
 * 测验lock锁的可重入性
 */
public class Locktest  implements   Runnable {
    ReentrantLock  lock= new  ReentrantLock();

    public void  set() throws InterruptedException {
        lock.lock();//上锁
        Thread.sleep(2000);
        System.out.println("我是set方法");
        lock.unlock();
    }
    public void  get() throws InterruptedException {
        lock.lock();
        Thread.sleep(2000);
        set();
        System.out.println("我是get方法");
        lock.unlock();
    }

    public void run() {
        try {
            get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        Locktest locktest = new Locktest();
        new  Thread(locktest).start();
        new  Thread(locktest).start();
    }
}

 

五、读写锁

相比Java中的锁(Locks in Java)里Lock实现,读写锁更复杂一些。假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。Java5在java.util.concurrent包中已经包含了读写锁。

代码演示:

package com.junlaninfo.ReadWriteLock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 /** * Created by 辉 on 2020/5/24.  */

public class Cache {
    static Map<String, Object> map = new HashMap<String, Object>();
   static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
   static Lock r = rwl.readLock();
   static Lock w = rwl.writeLock();

   // 获取一个key对应的value
   public static final Object get(String key) {
      r.lock();
      try {
         System.out.println("正在做读的操作,key:" + key + " 开始");
         Thread.sleep(100);
         Object object = map.get(key);
         System.out.println("正在做读的操作,key:" + key + " 结束");
         System.out.println();
         return object;
      } catch (InterruptedException e) {

      } finally {
         r.unlock();
      }
      return key;
   }

   // 设置key对应的value,并返回旧有的value
   public static final Object put(String key, Object value) {
      w.lock();
      try {

         System.out.println("正在做写的操作,key:" + key + ",value:" + value + "开始.");
         Thread.sleep(100);
         Object object = map.put(key, value);
         System.out.println("正在做写的操作,key:" + key + ",value:" + value + "结束.");
         System.out.println();
         return object;
      } catch (InterruptedException e) {

      } finally {
         w.unlock();
      }
      return value;
   }

   // 清空所有的内容
   public static final void clear() {
      w.lock();
      try {
         map.clear();
      } finally {
         w.unlock();
      }
   }

   public static void main(String[] args) {
      new Thread(new Runnable() {

         @Override
         public void run() {
            for (int i = 0; i < 10; i++) {
               Cache.put(i + "", i + "");
            }

         }
      }).start();
      new Thread(new Runnable() {

         @Override
         public void run() {
            for (int i = 0; i < 10; i++) {
               Cache.get(i + "");
            }

         }
      }).start();
   }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章