Java并发编程实战笔记—— 并发编程1
1、如何创建并运行java线程
创建一个线程可以继承java的Thread类,或者实现Runnabe接口。
public class thread {static class MyThread1 extends Thread{@Overridepublic void run() {System.out.println("run a myThread1");}}static class MyThread2 implements Runnable{@Overridepublic void run() {System.out.println("run a myThread2");}}public static void main(String[] args){MyThread1 myThread1 = new MyThread1();myThread1.start();Thread myThread2 = new Thread(new MyThread2());myThread2.start();} }
输出:
run a myThread1 run a myThread2
或者是创建一个实现了Runnable接口的匿名类
public class thread {public static void main(String[] args){new Thread(new Runnable() {@Overridepublic void run() {System.out.println("run a myThread3");}}).start();} }
输出:
run a myThread3
2、安全性
线程安全性可能是非常复杂的,在没有充足同步的情况下,多个线程的操作执行顺序是不同的,会产生非常奇怪的结果
class Fun{private int value;public int getNext(){return value++;} }
这个类中的getNext()是一个非线程安全的方法。如果直接在线程中调用时:
public class thread {public static void main(String[] args) {Fun fun = new Fun();for (int j = 0; j < 10; j++) {new Thread() {@Overridepublic void run() {System.out.println(fun.getNext());}}.start();}} }
输出:
0 1 2 3 4 5 6 7 8 9
结果是未知的,每一次运行的结果都不一样,这是因为递增运算value++看上去是一个单独的操作,但实际上它包含了三个独立的操作:读取value,将value加1,将结果写入value。由于运行时可能是多个线程交替执行的这就可能会导致当线程1还没将计算结果存入value时,线程2已经启动并已经读取了value的值。
java提供了各种同步机制来协同这种访问,比如这样修改这个类:
class Fun{private int value;public synchronized int getNext(){return value++;} }
这次main函数的输出是确定的:
0 1 2 3 4 5 6 7 8 9
一个对象是否是需要线程安全,取决于它是否被多个线程访问,这里注重的是在程序中访问对象的方式,而不是对象想要实现的功能。要使得对象是线程安全的,需要采用同步机制来协同对对象可变状态的访问。java中的主要同步机制synchronized,它提供一种独占的加锁方式,另外还有volatile类型的变量,显式锁,原子变量。
线程安全的定义:
当多个线程访问某个类的时候,不管运行时环境采取何种调度方式,并且在主调代码中不需要任何额外的同步或者协同,这个类都可以表现出正确的行为,那么就称这个类是线程安全的。
3、竞态条件
在并发编程中,由于不恰当的执行时序而出现不正确的结果,称作:竞态条件
class Fun{private Object instance = null;public Object getNext(){if (instance == null)instance = new Object();return instance;} }
这是一个最常见的竞态条件类型:“先检查后执行”,通过一个可能失效的观测结果来决定下一步的动作。这里的目的是将对象初始化的操作推迟到执行的时候才初始化,并确保只被初始化一次。
假设线程A和线程B需要同时执行getNext(),A看到instance为空,从而创建一个新的Object实例,B同样需要判断instance是否为空,但是线程B到底需不需要实例化,取决于不可确定的时序,线程的调度方式,以及A需要多长时间进行初始化。
要想避免竞态条件问题,就必须在某个线程修改某个变量时,通过某种方式防止其他线程使用这个变量,从而确保其他线程只能在修改完成之前或是之后读取和修改状态。
所以,假定有两个操作A和B,如果从执行A的线程来看,当另一个线程执行B时,要么等待B全部执行完,要么完全不执行B,那么A和B对彼此而言都是原子的。原子操作是指,对于访问同一个状态的所有操作来说,这个操作是以原子的方式执行操作的。
4、加锁机制
JAVA提供了一种内置的锁机制来支持原子性:同步代码块。同步代码块分为两个部分:一个作为锁得对象引用,一个作为又这个锁保护的代码块。以关键字synchronized来修饰的方法就是一种横跨整个方法体的同步代码块,其中该同步方法块的所就是方法调用所在的对象,
synchronized(lock){ //访问或修改由锁保护的共享状态 }
每个JAVA对象,都可以用一个实现同步的锁,线程进入同步代码块之前自动获得锁,并且在退出同步代码块时自动释放锁,获得内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。
当线程A尝试获取一个由线程B持有的锁时,线程A必须等待或者阻塞,直到线程B释放这个锁。如果B不释放这个锁,A也将永远等下去。
由于一次只能由一个线程执行内置锁持有的代码块,所以由这个锁保护的代码块会以原子方式执行。
5、可见性
在单线程环境中,如果向某个变量先写入值,然后 在没有其他写入操作的情况下读取这个变量,那么总能得到正确的值,但是在多线程环境中,我们无法保证执行读操作的线程能适时的看到其他线程写入的值,有时是根本不可能的事。为了保证多个线程之间对内存写入操作得可见性,必须使用同步机制。
public class Fun {private static boolean ready;private static int number;private static class ReaderThread extends Thread{public void run(){while (!ready)Thread.yield();System.out.println(number);}}public static void main(String[] args) {new ReaderThread().start();number = 42;ready = true;} }
Fun可能会永远持续循环下去,因为读线程可能永远都看不到ready得值,也可能是一直输出时0,因为可能看到了写入ready的值,没有看到number的值
6、Volatile变量
这是Java的另一种同步机制,volatile变量,用来确保将变量的更新操作通知到其他的线程,把变量声明为volatile类型之后,编译器运行时会注意到这个变量是共享的,因此不会把这个变量上的操作与其他内存操作一起进行重排序,volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此对volatile类型的变量总会返回最新写入的值。
写入volatile变量相当于退出了同步代码块,而读取volatile变量等于进入了同步代码块。
仅当volatile变量用作验证的时候,才应该使用它,例如,volatile的语义并不足以确保递增操作(count++)的原子性,除非能确保只有一个线程执行写操作,所以如果验证时对可见性进行复杂的判断,就不要使用volatile变量了。
7、发布和逸出
发布一个对象的意思就是指,是能够使对象在当前作用域之外的代码使用,比如:将一个指向该对象的引用保存到其他代码可以访问的地方,或者在某一处非私有的方法中返回该引用。在许多情况下,我们要确保对象及其内部状态不被发布,例如在对象构造完成之前就发布该对象,就会破坏线程安全性。当一个不该发布的对象被发布时,这种情况称为逸出。
首先来看一个对象是如何逸出的:
发布一个对象最简单的方法就是将对象得引用保存到一个公有的静态变量中,以便所有的类都能看见该类。
public static Set<Secret> knowSecrets;public void initialize(){knowSecrets = new HashSet<Secret>();}
在initialize方法中 实例化了一个新的HashSet对象,并将对象的引用保存在了knowSecrets中以发布该对象。
当发布某个对象的时候,可能间接地发布其他对象如果讲一个Secret对象添加到集合knowSecrets中,同样也会发布这个对象,因为任何代码都可以遍历这个集合,并获得新Secrect的引用。
class Unsafe{private String[] states = new String[]{"AK","AL"};public String[] getstates(){return states;}}
在这个实例中states原本是一个私有变量,因为getstates方法返回了它的引用,所以任何调用者都可以修改这个数组中的内容。
无论其他线程会对已发布的引用执行何种操作,其实都不重要,因为误用改引用的风险始终存在。
8、ThreadLocal类
ThreadLocal类是一种维持线程封闭性的规范方法,这个类能使使线程中的某个值与保存值的对象关联起来。ThreadLocal提供了get与set等访问接口或方法,这些方法为每个使用该变量的线程都存有一份独立的副本,因此get总是返回由当前执行线程在调用set时设置的最新值。
ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。例如,在单线程应用程序中可能会维持一个全局的数据库连接,并在程序初始化这个连接对象,从而避免在调用每个方法都要传递一个Connection对象。
当某个频繁执行的操作需要一个临时对象,例如一个缓冲区,而同时又希望避免在每次执行时都重新分配该临时对象,就可以使用ThreadLocal.
转载于:https://www.cnblogs.com/xxbbtt/p/7872598.html
Java并发编程实战笔记—— 并发编程1相关推荐
- Java并发编程实战笔记2:对象的组合
设计线程安全的类 在设计现车让安全类的过程之中,需要包含以下三步: 找出构成对象状态的所有变量 找出约束状态变量的不变性条件 建立对象状态的并发访问策略 实例封闭 通过封闭机制与合适的加锁策略结合起来 ...
- c++并发编程实战_Java 并发编程实战:JAVA中断线程几种基本方法
一个多线程Java程序,只有当其全部线程执行结束时(更具体地说,是所有非守护线程结束或者某个线程调用system.exit()方法的时候) ,才会结束运行.有时,为了终止程序或者取消一个线程对象所执行 ...
- java 8实战 异步社区_服!看完阿里大牛手写的Java异步编程实战笔记,我惊呆了...
这份笔记涵盖了Java中常见的异步编程场景,包括单JVM内的异步编程.跨主机通过网络通信的远程过程调用的异步调用与异步处理,以及Web请求的异步处理等. 在讲解Java中每种异步编程技术时都附有案例, ...
- Java并发编程实战笔记
如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误.有三种方式可以修复这个问题: i.不在线程之间共享该状态变量 ii.将状态变量修改为不可变的变量 iii.在访问状态变 ...
- Java编程思想笔记——并发3
终结任务 装饰性花园 公园人数共享计数值递增: class Count {private int count = 0;private Random rand = new Random(47);// R ...
- JAVA编程思想笔记 : 并发 [ 一 ]
程序中的所有事物,在任意时刻都只能执行一个步骤. 并发的多面性 并发解决的问题大体上可以分为"速度"和"设计可管理性"两种 并发通常是提高运行在单处理器上的程序 ...
- Java 高并发第二阶段实战---高并发设计模式,内存模型,CPU一致性协议,volatile关键字剖析
第二阶段的课程主要围绕着Volatile关键字,内存重排序,Happen-Before,Cpu一致性协议,高并发下的设计模式以及类加载器几个大的方面展开,下面是内容详细信息,本教程是本人录制,下载地址 ...
- 《java性能优化实战》之编程性能优化
目录 一.java编程性能优化实战 1.如何使用String.intern 节省内存 2.如何使用字符串的分割方法? 3.ArrayList还是LinkedList?使用不当性能差千倍 4.Strea ...
- java 编程思想 笔记_java编程思想笔记20170215
importjava.util.ArrayList;importjava.util.List;public classTest {public static voidmain(String[] arg ...
最新文章
- AI新作品:照片迅速被画成艺术画
- hector slam matlab,如何使用hector slam算法包
- 【Device Tree】设备树(一)——GPIO
- CoreAnimation编程指南(简介)
- fscanf不读取_思考了一天,终于把matlab的fscanf中的sizeA搞清楚了
- (个人使用)uni-app开发(官方资源)· 汇总
- python 类 对象 方法 应用_Python 定制类与其对象的创建和应用
- mac下chrome浏览器设置ajax跨域调试
- 怎样去提高效率,五步优化法
- 组合数学6--母函数与递推关系
- 猫狗数据集百度网盘链接
- 内构函数java_图灵学院笔记-java虚拟机底层原理
- 《posix多线程编程》笔记(四)
- linux下录音识别成文字软件下载,录音转文字分享助手
- # DZY Love Math 系列
- 软考是什么?软考有什么作用?
- 加强版Apktool堪称逆向神器
- Windows Workflow Foundation(1)- Windows Workflow Foundation 概述
- 关于fstream的flush
- u盘检测java软件_windows下的Java程序用于检测U盘
热门文章
- 【重磅】世界首例无创脑机接口,无大脑电极植入操控机器臂与飞行器
- 如何编写提供了IDisposable接口的类.
- Javascript实现的倒计时时钟
- java printwriter 没有关闭_Java PrintWriter close()方法
- 纯python好找工作吗_Python现在好找工作么?
- java 并_java并发编程(一)
- Java I/O体系之OutputStream和InputStream详细讲解
- 对java的final,finally,finalize应用场景,你用对了吗
- 在Ubuntu 18.04系统中使用Netplan工具配置网络
- 搭建CentOS7.6容器镜像