多线程爬坑之路--并发,并行,synchonrized同步的用法
一、多线程的并发与并行:
并发:多个线程同时都处在运行中的状态。线程之间相互干扰,存在竞争,(CPU,缓冲区),每个线程轮流使用CPU,当一个线程占有CPU时,其他线程处于挂起状态,各线程断续推进。
并行:多个线程同时执行,但是每个线程各自有自己的CPU,不存在CPU资源的竞争,他们之间也可能存在资源的竞争。
并发发生在同一段时间间隔内,并行发生在同一时刻内。并发执行的总时间是每个任务的时间和,而并行则取决于最长任务的时间。
下面看一下A,B两个任务在并行和并发情况下是怎么执行的:[不考虑其他资源竞争,只考虑CPU竞争]
A任务:a+b+c,
A任务有三步:a=1+1,b=2+2,c=3+3
B任务:x+y+z,
A任务有三步:x=1*1,y=2*2,z=3*3
A,B并行操作:多核CPU,多任务并行。
CPU1:
CPU2:
图中可以看到A,B操作相互不受影响。
当A任务开始的时候B任务也开始,他们可以同一时刻开始,如果每一个小任务耗时相同,那么他们可能同时结束。
A,B并发操作:单核cpu,多任务并发。
图中可以看出A,B同时执行的时候,一定有一个先,有一个后,因为CPU只有一个执行了A就不能执行B,但是为了让两个任务能够“同时完成“,CPU先执行A的一部分,在执行B的一部分,当这个间隔时间非常短的时候我们看到的就是A,B都在运行。
举个简单的例子:
左右手同时握住两支笔,并排点一个点,点出一条线来,这就是并行。只有一只手握住一支笔,左点一下,右点一下知道画出两条线,这两条线看似同时开始同时结束,实际是有时间差的,这就是并发。
实际操作中并不是严格的并发或者是并行,因为CPU有限,而任务是无限的。任务数量超过CPU核心数量的时候,就是并发并行共同存在的时候,不过这不是我们关注的重点,CPU资源的分配问题,是操作系统关心的重点。我们关心的重点是任务之间发生的资源竞争问题。
当多个线程对同一个资源进行访问和操作的时候就会出现数据一致性问题。一致性问题得不到解决多个任务的操作永远得不到正确的结果,解决一致性问题的方法就是同步机制。
二、synchonrized实现同步:
java每个对象都有一个内置锁,当用synchonrized修饰方法或者代码块的时候就会调用这个锁来保护方法和代码块。
同步方法:同步静态方法,同步非静态方法。
public synchronized void addMeethod2(){
num2++;
}
public static synchronized void addMeethod2(){
num2++;
}
同步代码块:
synchronized(Object){...}:Object表示一个对象,synchronized获取这个对象的同步锁,保证线程获取object的同步锁之后其他线程不能访问object的任何同步方法。但其他线程可以访问非同步的部分。
public void addMeethod3(){
synchronized(this){
num3++;
}
}
public void addMeethod4(){
synchronized(num4){
num4++;
}
}
public void addMeethod5(){
synchronized(Learnlocks.class){
num5++;
}
}
同步当前对象this:public void addMeethod3(){synchronized(this){num3++;}}和同步非静态方法:public synchronized void addMeethod2(){ num2++;}他们的效果是一样的都是作用与当前对象,即获取的object都是当前对象。那么只有这个线程可以操作这个对象所有synchronized修饰的部分,执行完这部分内容才会释放锁,其他线程才能访问。
下面看那一下示例:模拟三个线程分别对不同的同步机制保护的数据进行操作,看他们的具体表现。
import java.util.concurrent.atomic.AtomicInteger; public class NumAction {
private Integer num1=0;
private Integer num2=0;
private Integer num3=0;
private Integer num4=0;
private Integer num5=0;
private volatile Integer num6=0;
private AtomicInteger num7=new AtomicInteger(0); public NumAction() {
} //省略get/set方法public void Initializers(NumAction lk){
lk.num1=0;
lk.num2=0;
lk.num3=0;
lk.num4=0;
lk.num5=0;
lk.num6=0;
lk.num7=new AtomicInteger(0); }
//----------------------------------------------------------重点部分------------------------------------------------------
public void addMeethod1(){
num1++;
}
public synchronized void addMeethod2(){
num2++;
} public void addMeethod3(){
synchronized(this){
num3++;
}
} public void addMeethod4(){
synchronized(num4){
num4++;
}
} public void addMeethod5(){
synchronized(NumAction.class){
num5++;
}
} public void addMeethod6(){
num6++;
} public void addMeethod7(){
num7.incrementAndGet();
}
public void Add100() {
for (int i = 0; i < 100; i++) {
addMeethod1();
addMeethod2();
addMeethod3();
addMeethod4();
addMeethod5();
addMeethod6();
addMeethod7();
}
}
}
NumAction类:有七个属性,八个方法,前七个方法分别给七个属性自增一次。第八个方法是调用这七个方法100次。下面用多个线程来执行这个方法。
package com.eshore.ljcx.locks; public class Learnlocks2 extends Thread{
private static NumAction numaction=new NumAction();
public void run() {
numaction.Add100();
} public static void testRun(){
Learnlocks2 l1 = new Learnlocks2();
Learnlocks2 l2 = new Learnlocks2();
Learnlocks2 l3 = new Learnlocks2();
new Thread(l1).start();
new Thread(l2).start();
new Thread(l3).start();
try {
Thread.sleep(7000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(numaction.getNum1()+","+numaction.getNum2()+","+numaction.getNum3()+","+numaction.getNum4()+","+numaction.getNum5()+","+numaction.getNum6()+","+numaction.getNum7()); } public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
testRun();
numaction.Initializers(numaction);
}
}
}
我们启动了三个线程去执行给每个数据自增一百次的操作,理想状态下最终每个数据应该是300,再将这个步骤重复10次,看一下是不是一样的结果。
结果如下:num2,num3,num5,num7是300,其他的数据都不正确。说明这四个数据的同步起到了作用,他们分别是:(同步方法,同步当前对象,同步类对象,Atom原子对象。)而volatitle关键字被称为轻量级的同步机制并没有起到应有的效果。同步属性对象num4也并没有作用。
299,300,300,300,300,299,300
297,300,300,300,300,300,300
292,300,300,297,300,297,300
275,300,300,296,300,294,300
298,300,300,300,300,300,300
283,300,300,300,300,297,300
297,300,300,300,300,300,300
297,300,300,300,300,297,300
299,300,300,300,300,300,300
296,300,300,299,300,298,300
同步num4为什么没有起到作用:因为实际上我们上锁的是对象本身,并不是对象的引用。每次给对象自增,对象已经修改了,那么我们每次获取的锁也不是同一个锁。自然没有办法保持同步。如果我们添加一个不会被修改的属性对象num0。
private Integer num0 =0;
修改方法四:
public void addMeethod4(){
synchronized(this.num0){
num4++;
}
}
结果如下:发现num4的输出结果也是预期的300.
296,300,300,300,300,300,300
297,300,300,300,300,300,300
248,300,300,300,300,281,300
255,300,300,300,300,289,300
297,300,300,300,300,300,300
289,300,300,300,300,298,300
298,300,300,300,300,300,300
290,300,300,300,300,300,300
263,300,300,300,300,299,300
296,300,300,300,300,300,300
从num4可以看出来我们获取num0的对象锁,但是num4却可以保持同步。这是因为num0这个对象锁的代码块是num0对象锁的作用域,要对num4操作,必须获取num0对象锁。很多时候误区可能在于我们要对那个数据保持同步就要获取那个数据的锁,这是很错误的理解。首先synchonrized获取的是对象锁,数据不是对象就满足不了条件(个人见解,仅供参考)。
总结下:(synchonrized的用法在示例代码部分已经做了展示。)
//-------------------------------------------------------update:2017/3/23-------------------------------------------------
对于同步方法:
1、所有静态方法M0,M1。。:锁定的对象是类
多线程用类使用M0,M1会阻塞:M0,M1属于类,类加锁,锁相同
2、所有非静态方法M2,M3:锁定的对象是类的实例
多线程用不同的对象使用M2不会阻塞:对象不同,锁不同
多线程用相同的对象使用M2会阻塞:对象相同,锁相同
多线程用相同的对象使用M2,M3会阻塞:对象相同,锁相同
多线程用不同的对象使用M2,M3不会阻塞:对象不同,锁不同
*&*:
多线程访问M1(静态方法)和M2(非静态方法)不会阻塞:M1的锁是类,M2的锁是对象,锁不同
对于同步代码块:
1、非静态方法代码块:锁定的是指定的对象Object
同步代码块当Object==this的时候,和同步方法效果相同,但是如果只作用了整个方法的一部分代码块,那么效率会更高。【作用内容大小带来的差异】
同步代码块当Object==X(X==指定一个不变的对象 final Integer X=0)的时候,和同步方法效果不同,不同点在于此时作用域是所有以X为锁的代码块,而同步方法会作用于对象下所有的同步方法。这也是同步代码块相较于同步方法效率会更高的原因。【作用方法的数量带来的差异】
2、静态方法代码块:锁定的是指定的对象Object
同步代码块当Object==this的时候,不多说,还是类(静态方法类调用,怎么也扯不到实例对象上去)
同步代码块当Object==X(X==指定一个不变的对象 static final Integer X=0,X必须是静态的,因为静态方法只能使用静态属性)的时候,锁的作用域是所有使用X作为锁的代码块。而同步静态方法作用域是所有的同步方法。
//-------------------------------------------------------------------------------------------------------------------------
并发机制提高任务效率,而同步机制是保证并发的安全,但是同时也会破坏并发效率,所以同步的锁的粒度把握是个关键问题,在保证同步的情况下尽量保证性能才是关键。
对于非静态字段中可更改的数据,通常使用非静态方法访问.
对于静态字段中可更改的数据,通常使用静态方法访问,也可用非静态访问。不过是操作类。
参考:http://www.cnblogs.com/csniper/p/5478572.html。。。。。。。。
多线程爬坑之路--并发,并行,synchonrized同步的用法的更多相关文章
- [Java多线程]-并发,并行,synchonrized同步的用法
一.多线程的并发与并行: 并发:多个线程同时都处在运行中的状态.线程之间相互干扰,存在竞争,(CPU,缓冲区),每个线程轮流使用CPU,当一个线程占有CPU时,其他线程处于挂起状态,各线程断续推进. ...
- 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- 多线程爬坑之路-ThreadLocal源码及原理的深入分析
ThreadLocal<T>类:以空间换时间提供一种多线程更快捷访问变量的方式.这种方式不存在竞争,所以也不存在并发的安全性问题. This class provides thread-l ...
- 多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析
Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...
- 多线程爬坑之路-Thread和Runable源码解析
多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...
- Vue 爬坑之路(六)—— 使用 Vuex + axios 发送请求
Vue 原本有一个官方推荐的 ajax 插件 vue-resource,但是自从 Vue 更新到 2.0 之后,官方就不再更新 vue-resource 目前主流的 Vue 项目,都选择 axios ...
- Vue 爬坑之路(九)—— 用正确的姿势封装组件
迄今为止做的最大的 Vue 项目终于提交测试,天天加班的日子终于告一段落... 在开发过程中,结合 Vue 组件化的特性,开发通用组件是很基础且重要的工作 通用组件必须具备高性能.低耦合的特性 为了满 ...
- Vue 爬坑之路(一)—— 使用 vue-cli 搭建项目
vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目,GitHub地址是:https://github.com/vuejs/vue-cli vue ...
随机推荐
- 2.13. 获取托管对象(Core Data 应用程序实践指南)
用NSFetchRequest获取NSArray,里面都是托管对象.如果上下文里没有数据,就会从持久化存储区里获取. NSFetchRequest *request = [NSFetchRequest ...
- RAC执行root.sh报libcap.so.1: cannot open shared object file
Failed to create keys in the OLR, rc = 127, Message: /opt/app/11.2.0/grid/bin/clscfg.bin: error whil ...
- SVG的path的使用
SVG的path的使用: 参考:http://justcoding.iteye.com/blog/2226354 <%@ page language="java" conte ...
- Java高级特性之泛型
首先我们先提出两个问题: 什么是泛型? 为什么要使用泛型?我们先来看看第一个问题什么是泛型.如果你对Java三大特性中的多态性理解的比较透彻的话,泛型就比较好理解了.多态性表示一个对象具备多种状态.比 ...
- Android开发系列之Context
相信大家对于Context应该非常熟悉,但是Context到底是什么意思呢?到底指的是什么东西呢?我们可以理解为当前对象在程序中所处的一个环境,一个与系统交互的过程.Android系统的上下文对象,即 ...
- MySQL锁详解
一.概述 数据库锁定机制简单来说就是数据库为了保证数据的一致性而使各种共享资源在被并发访问访问变得有序所设计的一种规则.对于任何一种数据库来说都需要有相应的锁定机制,所以MySQL自然也不能例外.My ...
- pytho查找斐波那契序列中的值
''' 实现斐波那契序列,查找其中第N个数的值 ''' def FeiBSequence(list,N): length=len(list); i=0; while i<length: if N ...
- POJ2796(单调栈)
Feel Good Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 12987 Accepted: 3639 Case T ...
- 初识 BFC、 IFC、GFC、FFC
首先本文中介绍的 BFC. IFC.GFC.FFC 均为 CSS 中常见问题的解读,如没兴趣,可以绕道了. 然后在介绍这么多的 *FC 之前,我们得了解 一下 Box 和 Formatting Con ...
- Modelbuilder进阶教程
Modelbuilder进阶教程 By 李远祥 Modelbuilder 进阶1 自定义变量 参数是用来交互操作的,因此,参数具备非常大的灵活性,包括参数的定义和调用. 除了工具里面的参数之外,还可以 ...