ITPub博客

首页 > 应用开发 > Java > 好程序员Java培训分享Java多线程并发

好程序员Java培训分享Java多线程并发

原创 Java 作者:好程序员 时间:2020-07-14 11:38:19 0 删除 编辑

  好程序员Java 培训分享 Java 多线程并发, 1 Java 线程实现 / 创建方式

  继承 Thread

  Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start() 实例方法。 start() 方法是一个 native 方法,它将启动一个新线程,并执行 run() 方法。

 

public class MyThread extends Thread {

 

public void run() {

 

System.out.println("MyThread.run()");

 

} }

 

MyThread myThread1 = new MyThread();

 

myThread1.start();

 

实现 Runnable 接口

 

如果自己的类已经 extends 另一个类,就无法直接 extends Thread ,此时,可以实现一个 Runnable 接口。

 

public class MyThread extends OtherClass implements Runnable {

 

public void run() {

 

System.out.println("MyThread.run()");

 

} } // 启动

 

MyThreadMyThread myThread = new MyThread();

 

Thread thread = new Thread(myThread);

 

thread.start();

 

target.run()public void run() {

 

if (target != null) {

 

target.run();

 

} }

 

ExecutorService Callable Future 有返回值线程

 

有返回值的任务必须实现 Callable 接口,类似的,无返回值的任务必须 Runnable 接口。执行 Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到 Callable 任务返回的 Object 了,再结合线程池接口 ExecutorService 就可以实现传说中有返回结果的多线程了。

 

// 创建一个线程池

 

ExecutorService pool = Executors.newFixedThreadPool(taskSize);

 

// 创建多个有返回值的任务

 

List<Future> list = new ArrayList<Future>();

 

for (int i = 0; i < taskSize; i++) {

 

Callable c = new MyCallable(i + " ");

 

// 执行任务并获取 Future 对象

 

Future f = pool.submit(c); list.add(f);

 

} // 关闭线程池 pool.shutdown();

 

// 获取所有并发任务的运行结果 for (Future f : list) {

 

// Future 对象上获取任务的返回值,并输出到控制台 System.out.println("res " + f.get().toString());

 

}

 

基于线程池的方式

 

线程和数据库连接这些资源都是非常宝贵的资源。如果每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。

 

// 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10);

 

while(true) { threadPool.execute(new Runnable() {

 

// 提交多个线程执行 @Override public void run() {

 

System.out.println(Thread.currentThread().getName() + " is running ..");

 

try {

 

Thread.sleep(3000);

 

}

 

catch (InterruptedException e) {

 

e.printStackTrace();

 

} } }); }}

 

2 同步锁与死锁

 

同步锁当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。Java 中可以使用 synchronized 关键字来取得一个对象的同步锁。

 

死锁何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。

 

3 线程池原理

 

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。主要特点为:线程复用; 控制最大并发数 ; 管理线程。

 

线程复用一个Thread 的类都有一个 start 方法。当调用 start 启动线程时 Java 虚拟机会调用该类的 run 方法。那么该类的 run() 方法中就是调用了 Runnable 对象的 run() 方法。我们可以继承重写 Thread 类,在其 start 方法中添加不断循环调用传递过来的 Runnable 对象。这就是线程池的实现原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个 Runnable 之前可以是阻塞的。

 

线程池的组成一般的线程池主要分为以下 4 个组成部分:

 

(1) 线程池管理器:用于创建并管理线程池。 (2) 工作线程:线程池中的线程。 (3) 任务接口:每个任务必须实现的接口,用于工作线程调度其运行。 (4) 任务队列:用于存放待处理的任务,提供一种缓冲机制。

 

Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor Executors ExecutorService ThreadPoolExecutor Callable Future FutureTask 这几个类。

 

ThreadPoolExecutor 的构造方法如下:

 

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}

 

corePoolSize :指定了线程池中的线程数量。

 

maximumPoolSize :指定了线程池中的最大线程数量。

 

keepAliveTime :当前线程池数量超过 corePoolSize 时,多余的空闲线程的存活时间,即多次时间内会被销毁。

 

unit keepAliveTime 的单位。

 

workQueue :任务队列,被提交但尚未被执行的任务。

 

threadFactory :线程工厂,用于创建线程,一般用默认的即可。

 

handler :拒绝策略,当任务太多来不及处理,如何拒绝任务。

 

拒绝策略线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。

 

JDK 内置的拒绝策略如下:

 

AbortPolicy :直接抛出异常,阻止系统正常运行。

 

CallerRunsPolicy :只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。

 

DiscardOldestPolicy :丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。

 

DiscardPolicy :该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这是最好的一种方案。

 

以上内置拒绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍无法满足实际需要,完全可以自己扩展 RejectedExecutionHandler 接口。

 

Java 线程池工作过程 (1) 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

 

(2) 当调用 execute() 方法添加一个任务时,线程池会做如下判断:

 

a) 如果正在运行的线程数量小于 corePoolSize ,那么马上创建线程运行这个任务 ;

 

b) 如果正在运行的线程数量大于或等于 corePoolSize ,那么将这个任务放入队列 ;

 

c) 如果这时候队列满了,而且正在运行的线程数量小 maximumPoolSize ,那么还是要创建非核心线程立刻运行这个任务 ;

 

d) 如果队列满了,而且正在运行的线程数量大于或等 maximumPoolSize ,那么线程池会抛出异常 RejectExecutionException

 

(3) 当一个线程完成任务时,它会从队列中取下一个任务来执行。

 

(4) 当一个线程无事可做,超过一定的时间 (keepAliveTime) 时,线程池会判断,如果当前运行的线程数大于 corePoolSize ,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/69913864/viewspace-2704333/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-03-20

  • 博文量
    342
  • 访问量
    155222