并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力。如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可 交互性将大大改善。现代的PC都有多个CPU或一个CPU中有多个核,是否能合理运用多核的能力将成为一个大规模应用程序的关键。

线程基本使用

  编写线程运行时执行的代码有两种方式:一种是创建Thread子类的一个实例并重写run方法,第二种是创建类的时候实现Runnable接口。当然,实现 Callable也算是一种方式,Callable和Future结合实现可以实现在执行完任务后获取返回值,而Runnable和Thread方式是无法获取任务执行后的结果的。

public class ThreadMain {
public static void main(String[] args) {
MyThread myThread = new MyThread();
new Thread(myThread).start(); new MyThreas2().start();
}
} // 第一种方式,实现Runable接口
class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread run...");
}
} // 第二种方式,继承Thread类,重写run()方法
class MyThreas2 extends Thread {
@Override
public void run() {
System.out.println("MyThread2 run...");
}
}

  一旦线程启动后start()方法会立即返回,而不会等待run()方法执行完毕后返回,就好像run方法是在另外一个cpu上执行一样。

注意:创建并运行一个线程所犯的常见错误是调用线程的run()方法而非start()方法,如下所示:

Thread newThread = new Thread(MyRunnable());
newThread.run(); //should be start();

  起初你并不会感觉到有什么不妥,因为run()方法的确如你所愿的被调用了。但是,事实上,run()方法并非是由刚创建的新线程所执行的,而是当前线程所执行了。也就是被执行上面两行代码的线程所执行的。想要让创建的新线程执行run()方法,必须调用新线程的start方法。

Callable和Future结合实现实现在执行完任务后获取返回值

public static void main(String[] args) {
ExecutorService exec = Executors.newSingleThreadExecutor();
Future<String> future = exec.submit(new CallTask()); System.out.println(future.get());
}
class CallTask implements Callable {
public String call() {
return "hello";
}
}

给线程设置线程名:

MyTask myTask = new MyTask();
Thread thread = new Thread(myTask, "myTask thread"); thread.start();
System.out.println(thread.getName());

  当创建一个线程的时候,可以给线程起一个名字。它有助于我们区分不同的线程。

volatile

  在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。它在某些情况下比synchronized的开销更小,但是volatile不能保证变量的原子性。

  volatile变量进行写操作时(汇编下有lock指令),该lock指令在多核系统下有2个作用:

  • 将当前CPU缓存行写回系统内存。
  • 这个写回操作会引起其他CPU缓存了改地址的数据失效。

  多CPU下遵循缓存一致性原则,每个CPU通过嗅探在总线上传播的数据来检查自己的缓存值是否过期了,当发现缓存对应的内存地址被修改,将对应缓存行设置为无效状态,下次对数据操作会从系统内存重新读取。更多volatile知识请点击深入分析Volatile的实现原理

synchronized

  在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了。

  Java中每一个对象都可以作为锁,当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

  • 对于同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前对象的Class对象。
  • 对于同步方法块,锁是Synchonized括号里配置的对象。

  synchronized关键字是不能继承的,也就是说基类中的synchronized方法在子类中默认并不是synchronized的。当线程试图访问同步代码块时,必须先获得锁,退出或抛出异常时释放锁。Java中每个对象都可以作为锁,那么锁存在哪里呢?锁存在Java对象头中,如果对象是数组类型,则虚拟机用3个word(字宽) 存储对象头,如果对象是非数组类型,则用2字宽存储对象头。更多synchronized知识请点击Java SE1.6中的Synchronized

线程池

  线程池负责管理工作线程,包含一个等待执行的任务队列。线程池的任务队列是一个Runnable集合,工作线程负责从任务队列中取出并执行Runnable对象。

ExecutorService executor  = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {
executor.execute(new MyThread2());
}
executor.shutdown();

Java通过Executors提供了4种线程池:

  • newCachedThreadPool:创建一个可缓存线程池,对于新任务如果没有空闲线程就新创建一个线程,如果空闲线程超过一定时间就会回收。
  • newFixedThreadPool:创建一个固定数量线程的线程池。
  • newSingleThreadExecutor:创建一个单线程的线程池,该线程池只用一个线程来执行任务,保证所有任务都按照FIFO顺序执行。
  • newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。

  以上几种线程池底层都是调用ThreadPoolExecutor来创建线程池的。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
  • corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
  • maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
  • keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
  • TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。,可以选择的阻塞队列有以下几种:
  • workQueue(任务队列):用于保存等待执行的任务的阻塞队列。
    • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
    • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
    • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
    • PriorityBlockingQueue:一个具有优先级得无限阻塞队列。

当提交新任务到线程池时,其处理流程如下:

  1. 先判断基本线程池是否已满?没满则创建一个工作线程来执行任务,满了则进入下个流程。
  2. 其次判断工作队列是否已满?没满则提交新任务到工作队列中,满了则进入下个流程。
  3. 最后判断整个线程池是否已满?没满则创建一个新的工作线程来执行任务,满了则交给饱和策略来处理这个任务。

参考:

  1、深入分析Volatile的实现原理

  2、Java SE1.6中的Synchronized

Java并发基础总结的更多相关文章

  1. Java 并发基础

    Java 并发基础 标签 : Java基础 线程简述 线程是进程的执行部分,用来完成一定的任务; 线程拥有自己的堆栈,程序计数器和自己的局部变量,但不拥有系统资源, 他与其他线程共享父进程的共享资源及 ...

  2. java并发基础(五)--- 线程池的使用

    第8章介绍的是线程池的使用,直接进入正题. 一.线程饥饿死锁和饱和策略 1.线程饥饿死锁 在线程池中,如果任务依赖其他任务,那么可能产生死锁.举个极端的例子,在单线程的Executor中,如果一个任务 ...

  3. java并发基础(二)

    <java并发编程实战>终于读完4-7章了,感触很深,但是有些东西还没有吃透,先把已经理解的整理一下.java并发基础(一)是对前3章的总结.这里总结一下第4.5章的东西. 一.java监 ...

  4. Java并发基础概念

    Java并发基础概念 线程和进程 线程和进程都能实现并发,在java编程领域,线程是实现并发的主要方式 每个进程都有独立的运行环境,内存空间.进程的通信需要通过,pipline或者socket 线程共 ...

  5. java并发基础及原理

    java并发基础知识导图   一 java线程用法 1.1 线程使用方式 1.1.1 继承Thread类 继承Thread类的方式,无返回值,且由于java不支持多继承,继承Thread类后,无法再继 ...

  6. 【搞定 Java 并发面试】面试最常问的 Java 并发基础常见面试题总结!

    本文为 SnailClimb 的原创,目前已经收录自我开源的 JavaGuide 中(61.5 k Star![Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识.欢迎 Sta ...

  7. Java并发基础框架AbstractQueuedSynchronizer初探(ReentrantLock的实现分析)

    AbstractQueuedSynchronizer是实现Java并发类库的一个基础框架,Java中的各种锁(RenentrantLock, ReentrantReadWriteLock)以及同步工具 ...

  8. Java并发基础:进程和线程之由来

    转载自:http://www.cnblogs.com/dolphin0520/p/3910667.html 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程. ...

  9. java并发基础(六)--- 活跃性、性能与可伸缩性

    <java并发编程实战>的第9章主要介绍GUI编程,在实际开发中实在很少见到,所以这一章的笔记暂时先放一放,从第10章开始到第12章是第三部分,也就是活跃性.性能.与测试,这部分的知识偏理 ...

  10. java并发基础(一)

    最近在看<java并发编程实战>,希望自己有毅力把它读完. 线程本身有很多优势,比如可以发挥多处理器的强大能力.建模更加简单.简化异步事件的处理.使用户界面的相应更加灵敏,但是更多的需要程 ...

随机推荐

  1. 【Win10 应用开发】自定义应用标题栏

    Win 10 app对窗口标题栏的自定义包括两个层面:一是只定义标题中各部分的颜色,如标题栏上文本的颜色.三个系统按钮(最大化,最小化,关闭)的背景颜色等:另一层是把窗口的可视区域直接扩展到标题栏上, ...

  2. 前端学PHP之PDO预处理语句

    × 目录 [1]定义 [2]准备语句 [3]绑定参数[4]执行查询[5]获取数据[6]大数据对象 前面的话 本来要把预处理语句和前面的基础操作写成一篇的.但是,由于博客园的限制,可能是因为长度超出,保 ...

  3. 读书笔记--SQL必知必会10--分组数据

    10.1 数据分组 使用分组可以将数据分为多个逻辑组,对每个组进行聚集计算. 10.2 创建分组 使用SELECT语句的GROUP BY子句建立分组. GROUP BY子句必须出现在WHERE之后,O ...

  4. 4.C#WinForm基础图片(显示和隐藏)

    要求: 软件上有一张图片,默认是隐藏的.用户在文本框中输入身份证号(131226198105223452),点击按钮,如果年龄大于18岁,则显示图片. 知识点: 取当前年份,Date Time Now ...

  5. MongoDB分组汇总操作,及Spring data mongo的实现

    转载请在页首注明作者与出处 一:分组汇总 1.1:SQL样例 分组汇总的应用场景非常多,比如查询每个班级的总分是多少,如果用关系形数据库,那么sql是这样子的 ),class from score g ...

  6. iOS UINavigationController(内容根据iOS编程编写)

    我们知道 UITabBarController 对象,可以通过使用该对象,用户可以切换不同的屏幕.当要切换的各个屏幕之间没有相互依存关系的时候,该对象可以很好的完成任务.但是当多个屏幕互有关系的时候, ...

  7. dicom网络通讯入门(2)

    第二篇,前面都是闲扯 其实正文现在才开始,这次是把压箱底的东西都拿出来了. 首先我们今天要干的事是实现一个echo响应测试工具 也就是echo 的scu,不是实现打印作业管理么.同学我告诉你还早着呢. ...

  8. MVC5+EF6+AutoMapper+Bootstrap打造在线博客(1.1)

    DAL层的三个Model类: 字典表:CFDict 用户表:CFUser 用户爱好表:CFUserHobby(关联cfuser表和cfdict表) CFUser表和CFUserHobby表是一对多关系 ...

  9. FPGA旋转编码器的实现

    module pmodenc( clk, rst_n, A, B, BTN,// A_Debounce,// B_Debounce,// BTN_Debounce,// Rotary_right,// ...

  10. xinetd cpu 100%

    今天,有个给客户试用的环境出现xinetd cpu 100%,而且连续运行很长时间了.之前也有环境发生过,今天排查解决了三四个问题,实在是查的身体都不舒服了,还没时间查这个问题... 知道的求解...