一、多线程的并发与并行:

并发:多个线程同时都处在运行中的状态。线程之间相互干扰,存在竞争,(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同步的用法的更多相关文章

  1. [Java多线程]-并发,并行,synchonrized同步的用法

    一.多线程的并发与并行: 并发:多个线程同时都处在运行中的状态.线程之间相互干扰,存在竞争,(CPU,缓冲区),每个线程轮流使用CPU,当一个线程占有CPU时,其他线程处于挂起状态,各线程断续推进. ...

  2. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  3. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  4. 多线程爬坑之路-ThreadLocal源码及原理的深入分析

    ThreadLocal<T>类:以空间换时间提供一种多线程更快捷访问变量的方式.这种方式不存在竞争,所以也不存在并发的安全性问题. This class provides thread-l ...

  5. 多线程爬坑之路-J.U.C.atomic包下的AtomicInteger,AtomicLong等类的源码解析

    Atomic原子类:为基本类型的封装类Boolean,Integer,Long,对象引用等提供原子操作. 一.Atomic包下的所有类如下表: 类摘要 AtomicBoolean 可以用原子方式更新的 ...

  6. 多线程爬坑之路-Thread和Runable源码解析

    多线程:(百度百科借一波定义) 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提 ...

  7. Vue 爬坑之路(六)—— 使用 Vuex + axios 发送请求

    Vue 原本有一个官方推荐的 ajax 插件 vue-resource,但是自从 Vue 更新到 2.0 之后,官方就不再更新 vue-resource 目前主流的 Vue 项目,都选择 axios ...

  8. Vue 爬坑之路(九)—— 用正确的姿势封装组件

    迄今为止做的最大的 Vue 项目终于提交测试,天天加班的日子终于告一段落... 在开发过程中,结合 Vue 组件化的特性,开发通用组件是很基础且重要的工作 通用组件必须具备高性能.低耦合的特性 为了满 ...

  9. Vue 爬坑之路(一)—— 使用 vue-cli 搭建项目

    vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目,GitHub地址是:https://github.com/vuejs/vue-cli vue ...

随机推荐

  1. Assembly

    Principles of Computer Organization and Assembly Language Using the JavaTM Virtual Machine http://it ...

  2. AnsiIO

    1.文件数据内容,元数据内容 i节点ls -l err.txt-rw-rw-r-- 1 csgec csgec 50 Jun 23 11:19 err.txt-:普通文件(文件类型)rw-:属主用户拥 ...

  3. IOS开发缓存机制之—本地缓存机制

    功能需求 这个缓存机制满足下面这些功能. 1.可以将数据缓存到本地磁盘. 2.可以判断一个资源是否已经被缓存.如果已经被缓存,在请求相同的资源,先到本地磁盘搜索. 3.可以判断文件缓存什么时候过期.这 ...

  4. shell 脚本浅入

    最常用的Linux命令和工具 目录下个文档:cd name 返回上个目录:cd .. 查看.编辑文本文件:查看文件:more, tail 编辑文件:vi 如编写脚本.sh vi shell.sh ...

  5. Hibernate调用带有输入参数,输出参数为cursor的存储过程

    一.Oracle创建表及存储过程 1.创建表T_MONITOR_DEVICE 创建后的表结构 2.创建存储过程 create or replace procedure ProcTestNew(v_mo ...

  6. [UWP]依赖属性1:概述

    1. 概述 依赖属性(DependencyProperty)是UWP的核心概念,它是有DependencyObject提供的一种特殊的属性.由于UWP的几乎所有UI元素都是集成于DependencyO ...

  7. ThreadLocal模式的原理

    在JDK的早期版本中,提供了一种解决多线程并发问题的方案:java.lang.ThreadLocal类.ThreadLocal类在维护变量时,实际使用了当前线程(Thread)中的一个叫做Thread ...

  8. JavaScript———从setTimeout与setInterval到AJAX异步

    setTimeout与setInterval执行 首先我们看一下以下代码打印结果 console.log(1); setTimeout(function() { console.log(2); },1 ...

  9. node Express安装与使用(一)

    首先放上官网地址 http://www.expressjs.com.cn/ 学会查阅官方手册,它是最好的资料. 1.Express安装 首先确定你已经安装了 Node.js,然后去你创建的项目目录下( ...

  10. java中File类的常用所有方法及其应用

    创建:createNewFile()在指定位置创建一个空文件,成功就返回true,如果已存在就不创建,然后返回false.mkdir()  在指定位置创建一个单级文件夹.mkdirs()  在指定位置 ...