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

并发:多个线程同时都处在运行中的状态。线程之间相互干扰,存在竞争,(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. C语言的指针使用

    今天老师总结了一下指针内容,感觉对理解指针有帮助, 1.大家在使用指针的时候容易忽略掉指针所在的位置.  假如定义一个变量int a=10: int *p: p=&a;    //p中存放变量 ...

  2. Java-io流入门到精通详细总结

    IO流:★★★★★,用于处理设备上数据. 流:可以理解数据的流动,就是一个数据流.IO流最终要以对象来体现,对象都存在IO包中. 流也进行分类: 1:输入流(读)和输出流(写). 2:因为处理的数据不 ...

  3. 用js来实现页面的换肤功能(带cookie记忆)

    用js来实现页面的换肤功能 js实现换肤功能的实现主要是通过利用js控制CSS来实现的.大致的实现原理是这样的, 1.先定义一个页面基本样式style.css来确定div的宽高等属性,使得整个页面的D ...

  4. SpringMVC+RestFul详细示例实战教程

    一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的jar包. 2.添加Web.xml配置文件中关于SpringMVC的配置 <!--conf ...

  5. spring-dwr注解整合

    注解配置 1.web.xml 只需将DwrServlet换为DwrSpringServlet(包名不同) 2.dwr类 3.applicationContext.xml 4.annotationCon ...

  6. [JQuery]JQuery选择器引擎Sizzle

    写代码过程中,发现使用JQuery选择器时,$('div.tooltip')和$('.tooltip')的结果不一样,怀疑和选择器的代码逻辑有关(事后证明是代码的低级错误,但是从查找原因的过程中,学到 ...

  7. oracle 12c安装详细教程

    数据库下载:www.orcale.com访问官网自行下载. 一,将下载好的两个压缩包在同一目录中进行解压(文件目录尽量用英文) 解压后如下使用管理员身份运行setup安装程序 注意:对于学习阶段,这一 ...

  8. 开发mis系统的技术

    一.b/s架构 b/s架构:就broser/server,浏览器/服务器的说法.服务器端要运行tomcat,提供链接数据库服务供java代码读写数据,这个可以在eclipse中配置运行.浏览器则解释j ...

  9. kali linux live(persistence)+PE+windows7安装多启动菜单优盘制作

    需要以下工具: 下载链接: 一.制作kali linux persistence 将32GU盘格式化为FAT32格式,使用win32diskimager将kalilinux镜像写入优盘,占用优盘3.4 ...

  10. Extjs mvc

    MVC的模式,模型(Models)和控制器(Controllers) Model模型 是字段和它们的数据的集合,例如User模型带有username和password字段,模型知道如何持久化自己的数据 ...