Java并发与多线程
1. 并发与并行
并发是指某个时间段内,多任务交替处理的能力;并行是指同时处理多任务的能力,多核CPU可以实现并行任务。
并发执行的特点:
(1)并发程序间相互制约:程序执行结果的相互依赖以及共享资源(如处理器、缓冲区)的竞争;
(2)并发程序的执行过程是断断续续的,程序需要记忆现场指令及执行点;
(3)并发数设置合理且CPU拥有足够的处理能力时,并发可以提高程序的运行效率。
2. 线程与进程
进程是系统进行资源分配和调度的基本单位,在Java中当我们启动main函数时其实就启动了一个JVM进程,main函数所在的线程就是这个进程中的一个线程,也称主线程。
线程是进程的一个实体,是进程的一个执行路径,一个进程中至少有一个线程 ,进程中多个线程共享进程的资源。由于真正占用CPU运行的是线程,所以进程是CPU分配的基本单位。
由上图可知,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈区域,用于保存程序的执行位置以及线程的局部变量。进程中的堆被进程中所有线程共享,主要存放new操作创建的对象实例。方法区也是线程共享的,用于存放JVM加载的类,常量及静态变量等信息。
3. 多线程与并发的基本概念
内存可见性:JVM中的内存空间,服务<-->进程,线程;
synchronized关键字:Java提供的原子性内置锁,同一时间只有一个线程能进行操作该关键字修饰的代码块
volatile关键字:解决共享变量的可见性问题,线程在写入变量时不会将值缓存在寄存器或者其他地方而是直接写入主内存;保证了值的可见性,不能保证操作的原子性,即适用于写入变量值不依赖变量当前值的场景。
原子性操作:进行一系列操作时要么全部执行,要么全部不执行
CAS(Compare And Swap)比较交换,循环比较,若一致则交换,否则循环直到一致再交换
指令重排:Java内存模型允许编译器和处理器对指令重排序以提高运行性能。对于无依赖性的代码在执行过程中可能会进行指令重排,导致在多线程中操作共享变量时可能会重排指令共享变量的执行顺序,造成结果异常,可使用volatile修饰变量,使此变量写之前的操作不会重排到写之后。
伪共享:CPU一级缓存或二级缓存同一行加载多个变量,在多线程并发执行时可能会产生多个变量的值同时改变,使缓存失效,需要退回到主内存中获取变量值,影响程序执行效率。可以使用@Contended注解解决伪共享问题,但是限制在Java核心类使用,若需在用户类路径下使用,需要添加JVM参数:-XX:RestrictContended,填充宽度默认为128,要自定义宽度可以设置-XX:ContendedPaddingWidth参数。
4. 线程的三种创建方式
a. 实现Runnable接口的run方法,可以继承其他类,任务无返回值
public class ThreadTest { //创建线程
MyThread thread = new Thread(new Runnable() {
public void run() {
System.out.println("I'm a child thread.");
}
}); thread.start(); }
b. 继承Thread类并重写run方法,不能继承其他类,任务无返回值
public static class ThreadTest { public static class MyThread extends Thread { @Override
public void run() {
System.out.println("I'm a child thread");
}
} public static void main(String[] args) {
//创建线程
MyThread thread = new MyThread(); //启动线程
thread.start();
}
}
c. 使用FutureTask方式,可以继承其他类,也可以拿到返回结果
public static class CallerTask implements Callable<String> { @Override
public String call() throws Exception {
return "hello";
} public static void main(String[] args) throws InterruptedException {
//创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new CallerTask()); //启动线程
new Thread(futureTask).start(); try {
//等待任务执行完毕,并返回结果
String result = futureTask.get();
System.out.println(result);
} catch (ExcutionException e) {
e.printStackTrace();
}
} }
5. 线程的五种状态
6. 线程通知与等待
a. wait()等待:调用该方法的线程被阻塞挂起,其他线程调用了该共享对象的notify()或notifyAll()方法正常返回,若调用该线程的interrupt()方法,抛出InterruptedException异常返回
b. notify()通知:一个线程调用共享对象的notify()方法后,会唤醒一个在该共享变量上调用wait()方法后被挂起的线程。
c. notifyAll()广播:唤醒阻塞在该共享变量上的所有线程。
7. 线程的其他基本方法
a. join 等待线程执行中止的方法:在线程1中使用线程2调用join方法会使线程1阻塞直到线程2执行结束后继续执行
b. sleep 调用线程会让出指定时间的CPU执行权,此时间段内线程被阻塞挂起,持有的锁并不释放。
c. yield 让出剩余时间片的CPU执行权,并未被阻塞,任处于就绪状态。
d. 线程中断
void interrupt():中断线程,线程B调用线程A的interrupt()方法设置线程A的中断标志为true,线程A并未被中断,仍继续执行,若A已被阻塞挂起则线程A会在被阻塞的地方抛出InterruptedException异常返回
boolean isInterrupted():检测当前是否被中断
boolean interrupted():检测当前线程是否被中断,若发现被中断,则会清除中断标志。
8.线程池
线程池主要解决两个问题:一是当执行大量异步任务时线程池能提供较好的性能,线程池中的线程是可复用的,减少创建和销毁线程的开销;二是线程池提供了一种资源限制和管理的手段,如限制线程的个数,动态新增线程等。
ThreadPoolExcutor:线程池提供了许多可调参数和可扩展接口,如newCachedThreadPool(线程池个数最多可达Integer.MAX_VALUE,线程自动回收),newFixedThreadPool(固定大小线程池),newSingleThreadExecutor(单个线程)。
ScheduledThreadPoolExecutor:可以指定一定延迟时间后或者定时进行任务调度的线程池。、
public static ThreadPoolTest{
//命名线程工厂
static class NamedThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix; NamedThreadFactory(String name) { SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); if (null == name || name.isEmpty()) {
name = "pool";
} namePrefix = name + "_" + poolNumber.getAndIncrement() + "-thread-";
} public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon) {
t.setDaemon(false);
} if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
} return t;
} } //设置线程名称
static final String THREAD_SAVE_ACCEPT = "THREAD-ACCEPT";
static final String THREAD_SAVE_PROCESS = "THREAD-PROCESS"; //创建线程池
static ThreadPoolExecutor excutorAccept = new ThreadPoolExecutor(5, 5, 1,
TimeUnit.MINUTES,
new LinkedBlockingQueue<>(),
new NamedThreadFactory(POOL_ACCEPT);
);
static ThreadPoolExecutor excutorProcess = new ThreadPoolExecutor(5, 5, 1,
TimeUnit.MINUTES,
new LinkedBlockingQueue<>(),
new NamedThreadFactory(POOL_PROCESS);
); public static void main(String[] args) {
excutorAccept.execute(new Runnable(){
public void run() {
System.out.println("接受用户链接线程");
}
});
} public static void main(String[] args) {
excutorProcess.execute(new Runnable(){
public void run() {
System.out.println("处理业务线程");
}
});
} excutorAccept.shutdown();
excutorProcess.shutdown();
}
ThreadLocal
ThreadLocal是一个工具类,具体存放变量的是线程的threadLocals。threadLocals是一个ThreadLocalMap类型变量,内部是一个Entry数组,Entry内部的value用来存放通过ThreadLocal的set方法传递的值。
如果在线程池中使用ThreadLocal变量,要注意调用ThreadLocal的remove方法清理线程对ThreadLocal对象及值的引用,使JVM能够回收内存。原因:线程池默认创建的线程均为用户线程,在不主动调用shutdown方法时,用户线程会一直存在,导致用户线程一直持有ThreadLocal变量,使JVM无法进行内存回收,导致内存泄露。
Java并发与多线程的更多相关文章
- Java并发和多线程(一)基础知识
1.java线程状态 Java中的线程可以处于下列状态之一: NEW: 至今尚未启动的线程处于这种状态. RUNNABLE: 正在 Java 虚拟机中执行的线程处于这种状态. BLOCKED: 受阻塞 ...
- Java 并发和多线程(一) Java并发性和多线程介绍[转]
作者:Jakob Jenkov 译者:Simon-SZ 校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...
- Java并发和多线程:序
近期,和不少公司的"大牛"聊了聊,当中非常多是关于"并发和多线程"."系统架构"."分布式"等方面内容的.不少问题, ...
- Java并发和多线程2:3种方式实现数组求和
本篇演示3个数组求和的例子. 例子1:单线程例子2:多线程,同步求和(如果没有计算完成,会阻塞)例子3:多线程,异步求和(先累加已经完成的计算结果) 例子1-代码 package cn.fansuni ...
- Java并发与多线程教程(2)
Java同步块 Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) ...
- Java并发与多线程教程(1)
Java并发性与多线程介绍 在过去单CPU时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务 ...
- java并发与多线程面试题与问题集合
http://www.importnew.com/12773.html https://blog.csdn.net/u011163372/article/details/73995897 ...
- Java并发和多线程3:线程调度和有条件取消调度
在第1篇中"并发框架基本示例",提到了Executors和ThreadPool.其中,还有个"定时调度"的方法,Executors.newScheduledTh ...
- Java并发和多线程1:并发框架基本示例
Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括ThreadPool,Executor,Executors,ExecutorService,Com ...
- Java并发和多线程(二)Executor框架
Executor框架 1.Task?Thread? 很多人在学习多线程这部分知识的时候,容易搞混两个概念:任务(task)和线程(thread). 并发编程可以使我们的程序可以划分为多个分离的.独立运 ...
随机推荐
- 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_3-3.Vidoe相关接口完善和规范协议
笔记 3 .Vidoe相关接口完善和规范协议 简介:完善相关接口,协议规范讲解 1.save接口保存对象 1)@RequestParam(value = "p ...
- Performance Analysis of Logs (PAL) Tool
Performance Analysis of Logs (PAL) Tool 背景 在众多的独立项目中,我们如何快速了解数据库(SQL Server)服务器的性能,以及数据库的基线情况是怎样的,或者 ...
- 归纳整理Python中的控制流语句的知识点
归纳整理Python中的控制流语句的知识点 Python 解释器在其最简单的级别,以类似的方式操作,即从程序的顶端开始,然后一行一行地顺序执行程序语句.例如,清单 1 展示了几个简单的语句.当把它们键 ...
- React Native项目实战
算是学习React Native的一次项目总结吧,目的还是提高自己. 包含的内容: 1>仿"美团"页面的实现; 2>封装项目中和自己常用的一些组件; 3>学习别人 ...
- 【转载】用jquery给select option 赋值
var dataList = [ "6211125886667895", "6211125886667892", "6211125886667897& ...
- k8s local volume 和host path volume的区别
k8s提供多种volume接口,其中local 和host path是容易混淆的两个接口.下面这篇文章解释了两者的区别: https://groups.google.com/forum/#!topic ...
- powerDesigner关联数据库显示中文注释
最近使用powerdesigner,遇到些问题,记录一下[安装过程就略过了] 一.安装odbc驱动 分享下驱动,百度网盘链接:https://pan.baidu.com/s/1UYPq_PEQkDOJ ...
- cobaltr strike入门使用教程-1
前言 Cobalt Strike分为服务端和客户端两个部分从而实现分布式操作,协同作战.工具有linux和windows版本. 1.创建服务端 找到解压目录进入 ./teamserver [IP] [ ...
- springboot整合es客户端操作elasticsearch(三)
继续上个随笔: 那么我们只需要修改controller中文件就可以完成相关操作 本次主要是对文档得操作: 更新文档: package com.cxy.elasticsearch.controller; ...
- C# 字典、集合、列表的时间复杂度
List列表是顺序线性表,Add操作是O(1)或O(N),因为List是动态扩容的,在未扩容之前,其Add操作是O(1),而在扩容的时候,Add操作是O(N)的.其Contains方法,是按照线性检索 ...