Java 并发系列之一
Java 并发系列之一
简单的总结了一些 Java 常用的集合之后,发现许多集合都针对多线程提供了支持,比如 ConcurrentHashMap 使用分段锁来提高多线程环境下的性能表现与安全表现。所以我打算接着对 Java 并发的相关内容做一个简单总结。
线程与进程
进程是操作系统分配资源的基本单位,也就是说进程是运行中的程序。
线程是进程中的基本执行单元,每个进程都至少拥有一个线程。线程不独立拥有操作系统资源,线程共享进程的操作系统资源。
处理并发问题为什么使用多线程而不是多进程,在我看来主要有两点。一是进程间通信难度大于线程间通信,会增加开发难度,二是线程间切换效率高于进程间切换,选择多线程更适合并发场景。
线程的生命周期
这里我们只简单介绍线程 new->Runnable->Running->Dead 这个流程,不考虑 Block 的情况。
- 当我们创建一个 Thread 对象时,这个线程就进入了 new 的状态。
- 当时机成熟,我们调用这个对象的 start() 方法时,这个线程就进入了 Runnable 的状态。
- 然后这个线程就会等待 CPU 资源,如果获取到 CPU 资源就会自动运行。
- 运行结束后线程就会进入到 Dead 状态。
需要注意的是一个 Thread 对象只有一次调用 start() 方法的机会,无论这个线程是否顺利执行结束。
创建线程
创建线程也就是线程生命周期中的 new 状态。在 Java 中创建线程有 3 中方式:
- 继承 Thread 类并重写 run 方法
- 实现 Runnable 接口
- 实现 Callable 接口
继承 Thread 类来创建线程
使用继承 Thread 的方式可以直接在 run 方法中利用 this 来获取当前线程的信息,而不需要通过 Thread 类的静态方法 currentThread() 方法来获取当前的线程。
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(this.getName()+"正在运行");
}
}
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
}
//输出结果:Thread-0 正在运行
如果想要快速实现一个匿名的类来执行某个简单操作的话,可以用下面的方式:
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
System.out.println(this.getName() + "正在执行");
}
}.start();
}
//输出结果:Thread-0 正在运行
实现 Runnable 接口来创建线程
由于 Java 是不支持多继承的,所以如果要继承 Thread 类以外的类,使用 Runnable 来实现或许是个不错的选择。
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在运行");
}
}
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
//输出结果:Thread-0 正在运行
其实 Runnable 接口只包含一个 run 方法,本质上还是通过重写 Thread 类的 run 方法来达到创建线程的目的。Runnable 还是一个函数式接口,所以想要声明一个匿名的 Thread 类还能通过下面的方式:
public static void main(String[] args) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "正在运行");
}).start();
}
//输出结果:Thread-0 正在运行
实现 Callable 接口来创建线程
上面两种创建线程的方式都是不支持返回值的,如果需要线程在执行之后提供返回值,就可以通过 Callable 来创建线程。使用 Callable 创建线程分为以下几个步骤:
- 创建类实现 Callable 接口的 call() 方法
- 使用 FutureTask 来包装上一步创建的类
- 使用 FutureTask 来创建 Thread 类
- 使用 FutureTask 对象的 get() 方法来获取返回值
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"正在运行");
return 316495132;
}
}
public static void main(String[] args)
throws ExecutionException,InterruptedException {
Callable callable = new MyCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
System.out.println("返回值为:" + futureTask.get());
}
//输出结果:
//Thread-0 正在运行
//返回值为:316495132
小结
- 如果没有特殊的需求,实现 Runnable 接口或许是一个比较好的选择
- 如果需要线程执行完成后提供返回值,就只能选择继承 Callable 接口
Java 并发系列之一的更多相关文章
- Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析
学习Java并发编程不得不去了解一下java.util.concurrent这个包,这个包下面有许多我们经常用到的并发工具类,例如:ReentrantLock, CountDownLatch, Cyc ...
- Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式
在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...
- Java并发系列[3]----AbstractQueuedSynchronizer源码分析之共享模式
通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...
- Java并发系列[5]----ReentrantLock源码分析
在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...
- Java 并发系列之二:java 并发机制的底层实现原理
1. 处理器实现原子操作 2. volatile /** 补充: 主要作用:内存可见性,是变量在多个线程中可见,修饰变量,解决一写多读的问题. 轻量级的synchronized,不会造成阻塞.性能比s ...
- java并发系列 - 第28天:实战篇,微服务日志的伤痛,一并帮你解决掉
这是java高并发系列第28篇文章. 环境:jdk1.8. 本文内容 日志有什么用? 日志存在的痛点? 构建日志系统 日志有什么用? 系统出现故障的时候,可以通过日志信息快速定位问题,修复bug,恢复 ...
- java并发系列 - 第29天:高并发中常见的限流方式
这是java高并发系列第29篇. 环境:jdk1.8. 本文内容 介绍常见的限流算法 通过控制最大并发数来进行限流 通过漏桶算法来进行限流 通过令牌桶算法来进行限流 限流工具类RateLimiter ...
- Java 并发系列之八:java 并发工具(4个)
1. CountDownLatch 2. CyclicBarrier 3. Semaphore 4. Exchanger 5. txt java 并发工具 通俗理解 CountDownLatch 等A ...
- Java 并发系列之十:java 并发框架(2个)
1. Fork/Join框架 2. Executor框架 3. ThreadPoolExecutor 4. ScheduledThreadPoolExecutor 5. FutureTask 6. t ...
随机推荐
- 升级本地已安装的 Node 和 npm 版本
Mac升级本地已经安装的NodeJs和Npm到最新版,可以使用一下方式进行升级和更新. 其实windos上升级nodejs也很简单,只需在nodejs官网下载安装最新的msi即可. 值得注意的是安装时 ...
- VRRP笔记三:配置keepalived为实现haproxy高可用的双主模型配置文件示例:
说明:其基本实现思想为创建两个虚拟路由器,并以两个节点互为主从. ! Configuration File for keepalived global_defs { notification_emai ...
- Nginx笔记总结二十:nginx索引目录配置
location / { autoindex on; autoindex_localtime on; }
- Java的同步和异步
同步:发送一个请求,等待返回,然后再发送下一个请求 异步:发送一个请求,不等待返回,随时可以再发送下一个请求 同步可以避免出现死锁,读脏数据的发生,一般共享某一资源的时候用,如果每个人都有修改权限,同 ...
- shell清除文件内容脚本
先来代码: [root@localhost Qingchu]# cat Qingchu.sh #!/bin/bash #描述: #作者:孤舟点点 #版本:1.0 #创建时间:-- :: PATH=/b ...
- 引入 JPEGCodec;JPEGImageEncoder; 图片处理;MyEclipse编译时报错处理
在Eclipse中处理图片,需要引入两个包: import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JP ...
- numpy array 分割
import numpy as np A = np.array([1,1,1])[:,np.newaxis] B = np.array([2,2,2])[:,np.newaxis] #合并 C = n ...
- scrapy爬虫框架教程(二)-- 爬取豆瓣电影
前言 经过上一篇教程我们已经大致了解了Scrapy的基本情况,并写了一个简单的小demo.这次我会以爬取豆瓣电影TOP250为例进一步为大家讲解一个完整爬虫的流程. 工具和环境 语言:python 2 ...
- postgresql学习记录1
数据库9.3.5,系统fedora20,不同系统操作略有不同. 使用yum 命令安装即可:sudo yum install postgresql,postgresql-server 安装完毕后系统中会 ...
- Ubuntu 14.04 下NFS安装配置
1.执行命令:sudo apt-get install nfs-kernel-server; 2.执行命令:mkdir /home/jack/nfs-share 建立一个nfs服务的专有的文件夹; 3 ...