java-并发编程

简介: 1、并发编程  并发通常能提高单处理器的程序性能。可是,看到这句话有些违背直觉。多线程的运行增加了线程间切换的开销,仅仅从这个角度看,单线程运行总比多线程的性能好。但是,程序的阻塞会使得结果不一样,当某个线程阻塞时候,其它线程仍然可以执行,因此程序仍保持运行。

1、并发编程

  并发通常能提高单处理器的程序性能。可是,看到这句话有些违背直觉。多线程的运行增加了线程间切换的开销,仅仅从这个角度看,单线程运行总比多线程的性能好。但是,程序的阻塞会使得结果不一样,当某个线程阻塞时候,其它线程仍然可以执行,因此程序仍保持运行。充分利用cpu的时间提高的性能远大于线程间的切换带来的性能下降。

何为阻塞:程序中某个线程由于在不满足某些条件的情况下而导致不能够继续执行的现象

2、基本线程机制:

  一个程序可以分为多个独立运行任务,每个独立任务由线程驱动执行。线程并发执行,表面看起来是同时执行,好像各自都有一个自己的CPU一样。实际上,底层机制是CPU将时间片分给各个线程,一个时间只能有一个线程获得CPU的时间片,也就是线程独占CPU资源。

3、定义任务类、定义线程类

定义任务

package com.duoxiancheng;

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

public class Main {

public static void main(String[] args) {

for (int i=0; i<2; i++) {
new Thread(new LiftOff()).start();
}
}
}

class LiftOff implements Runnable{
private int countDown=3;
private static int taskCount=0;
private final int id=taskCount++;

@Override
public void run() {
while (countDown-->0) {
System.out.println("#id=" + id + " countDown="+(countDown > 0 ? countDown : "LiftOff!"));
Thread.yield();
}
}
}

输出结果

#id=0 countDown=2
#id=1 countDown=2
#id=0 countDown=1
#id=1 countDown=1
#id=0 countDown=LiftOff!
#id=1 countDown=LiftOff!

定义线程类

package com.duoxiancheng;

public class Main {

> > public static void main(String[] args) {

for (int i=0; i<2; i++) {
new LiftOff().start();
}
}
}

class LiftOff extends Thread{
private int countDown=3;
private static int taskCount=0;
private final int id=taskCount++;

@Override
public void run() {
while (countDown-->0) {
System.out.println("#id=" + id + " countDown="+(countDown > 0 ? countDown : "LiftOff!"));
Thread.yield();
}
}
}

4、线程池

待续...

5、从任务中产生返回值---Callable<T>接口

package com.duoxiancheng;

import java.util.concurrent.*;

public class Main1 {
public static void main(String[] args) throws Exception{

    ExecutorService executorService= Executors.newCachedThreadPool();
    for (int i=0;i&lt;50;i++){
        Future future = executorService.submit(new TaskWithResult(i));
        Object result = future.get();
        System.out.println(result);
    }
}

}
class TaskWithResult implements Callable{
private int id=0;
public TaskWithResult(int id){br/>this.id=id;
}
@Override
public Object call() throws Exception {
return id;
}
}

其中 future.get()是阻塞的方法;如果想想立即阻塞任务的等待,则可以使用 result = exec.submit(aCallable).get(); 形式

6、常用方法

休眠---sleep()

让步---yield()

加入一个线程---join()

优先级--setPriority()/getPriority()

后台线程--setDaemon()/isDaemon()

...

7、捕获线程中的异常

线程中抛出异常,会传播到控制台,除非采用特殊手段。

public interface Runnable {
public abstract void run();
}
在run()方法内部try-catch-finally捕获异常
使用异常处理器捕获异常--异常处理器实现Thread.UncaughtExceptionHandler接口
以下分析自定义异常处理器:

为线程设置异常处理器。具体做法可以是以下几种:
(1)Thread.setUncaughtExceptionHandler设置当前线程的异常处理器;
(2)Thread.setDefaultUncaughtExceptionHandler为整个程序设置默认的异常处理器;
如果当前线程有异常处理器(默认没有),则优先使用该UncaughtExceptionHandler类;否则,如果当前线程所属的线程组有异常处理器,则使用线程组的
UncaughtExceptionHandler;否则,使用全局默认的DefaultUncaughtExceptionHandler;如果都没有的话,子线程就会退出

package com.duoxiancheng;

import java.util.concurrent.ThreadFactory;

public class Main2 {

public static void main(String[] args) {

    Thread thread=new Thread(new ExceptionThread());
    thread.setUncaughtExceptionHandler(new MyExceptionHandler());
    thread.start();
}

}

/**

  • 任务类
    */
    class ExceptionThread implements Runnable{

    @Override
    public void run() {
    Thread t = Thread.currentThread();
    System.out.println("ExceptionThread 当前线程信息:"+t.toString());
    System.out.println("当前线程ExceptionThread的异常处理器"
    +t.getUncaughtExceptionHandler());
    throw new RuntimeException();
    }
    }

/**

8、共享资源

共享资源竞争:
  导致线程安全问题

解决思想:
  多人(线程)都希望单独使用浴室(共享资源)。为了使用浴室,一个人先敲门,看能不能使用。如果没人回话,他就进入浴室并锁上门(获得锁)。这时候,其它人想使用浴室的话,就会被阻挡在外面(不能获取锁),直到浴室可以使用。浴室外面的人没有排队,浴室门打开(前一个人释放锁),离门最近的人优先进入使用(获得锁,设置优先级和yield方法可以建议某个优先使用)。

解决方式:
  Synchronized 、Lock锁同步以及Voliate修饰符和原子类

  线程本地存储---ThreadLocal

9、线程之间协作

生产者与消费者

package com.duoxiancheng;

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

/**

  • 生产者-消费者
    */
    public class Main3 {

    public static void main(String[ ] args) throws InterruptedException {
    Restaurant restaurant=new Restaurant();
    new Thread(new ProductorThread(restaurant)).start();
    Thread.sleep(20);
    new Thread(new ConsumerThread(restaurant)).start();
    }
    }
    class Restaurant {

    Lock lock=new ReentrantLock();//锁
    Condition condition1=lock.newCondition();//条件1
    Condition condition2=lock.newCondition();//条件2

    private int count;//已做好的餐

    private int count2;

    /**

    • 消费者方法
      */
      public void comsumer(){
      lock.lock();
      try {
      if (count==0) {
      System.out.println(Thread.currentThread().getId()+"客户 想要一份快餐!");
      condition2.signalAll();
      System.out.println(Thread.currentThread().getId()+"客户 等待一份快餐!");
      condition1.await();
      }
      count--;
      System.out.println(Thread.currentThread().getId()+ "客户 消费了一份快餐!");
      } catch (InterruptedException e) {
      e.printStackTrace();
      }finally {
      lock.unlock();
      }
      }
      /**
    • 生产者方法
      */
      public void productor(){
      lock.lock();
      try {
      condition2.await();
      count++;//生产一份快餐
      System.out.println(Thread.currentThread().getId()+ "厨师 制作了一份快餐!");
      condition1.signalAll();
      System.out.println(Thread.currentThread().getId()+"厨师 通知客户使用");
      }catch (InterruptedException e){
      e.printStackTrace();
      }finally {
      lock.unlock();
      }
      }
      }

/**

  • 消费者
    */
    class ConsumerThread implements Runnable{

    private Restaurant restaurant;

    public ConsumerThread(Restaurant restaurant){
    this.restaurant=restaurant;
    }

    @Override
    public void run() {
    restaurant.comsumer();
    }
    }

/**

  • 生产者
    */
    class ProductorThread implements Runnable{

    private Restaurant restaurant;

    public ProductorThread(Restaurant restaurant){
    this.restaurant=restaurant;
    }

    @Override
    public void run() {
    restaurant.productor();
    }
    }

输出结果:

11客户 想要一份快餐!
11客户 等待一份快餐!
10厨师 制作了一份快餐!
10厨师 通知客户使用
11客户 消费了一份快餐!

生产者与消费者 和 队列
  使用wait()、notifyAll() 是一种解决任务互操作问题非常低级的方式。使用同步队列来解决任务协作问题,同步队列在任何时刻只允许一个任务插入或移除。

java.util.concurrent.BlockingQueue接口提供了这个同步队列,其有大量的实现。通常可以使用LinkedBlockingDeque(×××队列) 和 ArrayBlockingDeque(固定尺寸队列)。

消费者任务试图从队列中获取对象,而该队列此时为空,那这些队列还可以挂起消费者任务(阻塞);当有更多元素可用时恢复消费者任务。阻塞队列可以解决非常大量的问题。

package com.duoxiancheng;

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

/**

  • 生产者-消费者与队列
    */
    public class Main4 {

    public static void main(String[] args) {

    ExecutorService executorService= Executors.newCachedThreadPool();
    
    LinkedBlockingDeque&lt;Toast&gt; toastQueue=new LinkedBlockingDeque&lt;&gt;();
    LinkedBlockingDeque&lt;Toast&gt; butteredQueue=new LinkedBlockingDeque&lt;&gt;();
    LinkedBlockingDeque&lt;Toast&gt; jammedQueue=new LinkedBlockingDeque&lt;&gt;();
    
    executorService.execute(new Toaster(toastQueue));
    executorService.execute(new Butterer(toastQueue, butteredQueue));
    executorService.execute(new Jammed(butteredQueue, jammedQueue));
    executorService.execute(new Eater(jammedQueue));

    }
    }

class Toast{

private Status status=Status.DRY;
private final int id;
public Toast(int id) {
    this.id = id;
}
public int getId() {
    return id;
}
public Status getStatus() {
    return status;
}
public void addButtered(){
    status=Status.BUTTERED;
}
public void addJammed(){
    status=Status.JAMMED;
}
@Override
public String toString() {
    return "Toast "+id+" : "+status;
}

/**
 * 枚举类型
 */
public enum Status{
    DRY,BUTTERED,JAMMED
}

}

/**

/**

/**

/**

输出结果:

Toast 0 : DRY
添加黄油, Toast 0 : BUTTERED
添加果酱, Toast 0 : JAMMED
消费了一个吐司
Toast 1 : DRY
添加黄油, Toast 1 : BUTTERED
添加果酱, Toast 1 : JAMMED
消费了一个吐司
Toast 2 : DRY
添加黄油, Toast 2 : BUTTERED
添加果酱, Toast 2 : JAMMED
消费了一个吐司
Toast 3 : DRY
添加黄油, Toast 3 : BUTTERED
添加果酱, Toast 3 : JAMMED
消费了一个吐司
Toast 4 : DRY
添加黄油, Toast 4 : BUTTERED
添加果酱, Toast 4 : JAMMED
消费了一个吐司

10、死锁

某个线程在等待另一个线程释放锁,而后者又在等别的线程的所,这样一直下去,直到整个链条上的线程又在等第一个线程释放锁。这样线程之间的相互等待的连续循环,导致没有哪个线程能继续执行的现象。

死锁四大条件:

请求与保持

不可剥夺

互斥条件

循环等待

防止死锁:
发生死锁必须全部满足四大条件,要避免死锁,只需破坏其中一个即可。

目录
相关文章
|
4天前
|
Java
并发编程的艺术:Java线程与锁机制探索
【6月更文挑战第21天】**并发编程的艺术:Java线程与锁机制探索** 在多核时代,掌握并发编程至关重要。本文探讨Java中线程创建(`Thread`或`Runnable`)、线程同步(`synchronized`关键字与`Lock`接口)及线程池(`ExecutorService`)的使用。同时,警惕并发问题,如死锁和饥饿,遵循最佳实践以确保应用的高效和健壮。
13 2
|
8天前
|
Java 开发者
Java中的并发编程:从基础到高级
在Java的世界里,并发编程是一块既迷人又复杂的领域。它像是一场精心编排的交响乐,需要各种乐器和乐手的协同合作。本文将带领读者走进Java并发编程的殿堂,从简单的线程创建与管理开始,逐步深入到同步机制、锁的概念,最后探索Java内存模型和高级并发工具。我们将一起揭开并发编程神秘的面纱,理解其背后的原理,并掌握如何在实际开发中有效利用这些知识来编写高效、稳定的应用程序。
|
4天前
|
存储 缓存 Java
并发编程-Java内存模型到底是什么
并发编程-Java内存模型到底是什么
|
18小时前
|
Java 程序员 调度
Java并发编程之Executor框架深度解析
【6月更文挑战第24天】在Java的并发编程领域,Executor框架是处理多线程任务的核心。本文将深入探讨Executor框架的设计哲学、核心组件以及如何高效利用这一框架来提升程序的性能和响应性。我们将通过实例演示如何正确配置和使用Executor,并讨论常见的陷阱与最佳实践。
|
2天前
|
Java
Java并发编程:深入理解synchronized与ReentrantLock
【6月更文挑战第22天】本文将深入探讨Java并发编程中两个重要的同步机制:synchronized关键字和ReentrantLock类。我们将通过实例分析它们之间的差异,以及在实际应用中如何根据场景选择恰当的同步工具。
|
2天前
|
Java
Java并发编程中锁的释放
Java并发编程中锁的释放
12 1
|
5天前
|
安全 Java 开发者
深入理解Java内存模型(JMM)及其对并发编程的影响
【6月更文挑战第19天】在Java的世界中,内存模型是构建高效、线程安全应用的基石。本文将通过探讨Java内存模型(JMM)的核心概念和原理,揭示它如何影响并发编程实践。我们将从JMM的基本定义出发,逐步解析它在同步机制、可见性规则以及有序性保证方面的作用。同时,我们也将讨论JMM对现代Java开发中常见的并发模式和框架的影响。最后,文章会提供一些实际的编码建议和最佳实践,帮助开发者更好地利用JMM来设计并发程序。
|
5天前
|
存储 安全 算法
Java并发编程中的线程安全性与性能优化
在Java编程中,特别是涉及并发操作时,线程安全性及其与性能优化是至关重要的问题。本文将深入探讨Java中线程安全的概念及其实现方式,以及如何通过性能优化策略提升程序的并发执行效率。
9 1
|
6天前
|
安全 Java 调度
Java并发编程:优化多线程应用的性能与安全性
在当今软件开发中,多线程编程已成为不可或缺的一部分,尤其在Java应用程序中更是如此。本文探讨了Java中多线程编程的关键挑战和解决方案,重点介绍了如何通过合理的并发控制和优化策略来提升应用程序的性能和安全性,以及避免常见的并发问题。
12 1
|
7天前
|
安全 Java 程序员
Java并发编程中的锁机制与优化策略
【6月更文挑战第17天】在Java并发编程的世界中,锁是维护数据一致性和线程安全的关键。本文将深入探讨Java中的锁机制,包括内置锁、显式锁以及读写锁的原理和使用场景。我们将通过实际案例分析锁的优化策略,如减少锁粒度、使用并发容器以及避免死锁的技巧,旨在帮助开发者提升多线程程序的性能和可靠性。