java-并发编程

本文涉及的产品
私网连接 PrivateLink,5万GB流量 1.5万小时实例时长
简介: 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、死锁

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

死锁四大条件:

请求与保持

不可剥夺

互斥条件

循环等待

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

目录
相关文章
|
2月前
|
Java 编译器 开发者
深入理解Java内存模型(JMM)及其对并发编程的影响
【9月更文挑战第37天】在Java的世界里,内存模型是隐藏在代码背后的守护者,它默默地协调着多线程环境下的数据一致性和可见性问题。本文将揭开Java内存模型的神秘面纱,带领读者探索其对并发编程实践的深远影响。通过深入浅出的方式,我们将了解内存模型的基本概念、工作原理以及如何在实际开发中正确应用这些知识,确保程序的正确性和高效性。
|
7天前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
15 0
|
4月前
|
Java 程序员 调度
【JAVA 并发秘籍】进程、线程、协程:揭秘并发编程的终极武器!
【8月更文挑战第25天】本文以问答形式深入探讨了并发编程中的核心概念——进程、线程与协程,并详细介绍了它们在Java中的应用。文章不仅解释了每个概念的基本原理及其差异,还提供了实用的示例代码,帮助读者理解如何在Java环境中实现这些并发机制。无论你是希望提高编程技能的专业开发者,还是准备技术面试的求职者,都能从本文获得有价值的见解。
68 1
|
12天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
37 5
|
1月前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
40 2
|
13天前
|
设计模式 安全 Java
Java 多线程并发编程
Java多线程并发编程是指在Java程序中使用多个线程同时执行,以提高程序的运行效率和响应速度。通过合理管理和调度线程,可以充分利用多核处理器资源,实现高效的任务处理。本内容将介绍Java多线程的基础概念、实现方式及常见问题解决方法。
37 0
|
3月前
|
Java 开发者
深入探索Java中的并发编程
本文将带你领略Java并发编程的奥秘,揭示其背后的原理与实践。通过深入浅出的解释和实例,我们将探讨Java内存模型、线程间通信以及常见并发工具的使用方法。无论是初学者还是有一定经验的开发者,都能从中获得启发和实用的技巧。让我们一起开启这场并发编程的奇妙之旅吧!
32 5
|
3月前
|
算法 安全 Java
Java中的并发编程是如何实现的?
Java中的并发编程是通过多线程机制实现的。Java提供了多种工具和框架来支持并发编程。
19 1
|
3月前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
3月前
|
安全 Java 测试技术
掌握Java的并发编程:解锁高效代码的秘密
在Java的世界里,并发编程就像是一场精妙的舞蹈,需要精准的步伐和和谐的节奏。本文将带你走进Java并发的世界,从基础概念到高级技巧,一步步揭示如何编写高效、稳定的并发代码。让我们一起探索线程池的奥秘、同步机制的智慧,以及避免常见陷阱的策略。