一、共享资源竞争问题

在Java语言的并发编程中,由于我们不知道线程实际上在何时运行,所以在实际多线程编程中,如果两个线程访问相同的资源,那么由于线程运行的不确定性便会在这种多线程中产生访问错误。所以为了避免这一情况的发生,我们在编程的时候需要把并发执行的线程中用于访问这一共享资源的方法进行同步处理,以避免并发对于共享资源产生的影响。

并发模式在解决线程冲突的问题时,基本上都是采用序列化访问共享资源的方案。这在我的理解中,就是我们要控制同一时刻只能让一个线程对这一共享资源进行访问。

二、synchronized关键字的使用

1.synchronized对于类普通成员方法的修饰

Java语言中,每一个对象都含有单一的锁(监视器)。而synchronized的作用之一就是修饰使用了共享资源的成员方法,这样在线程通过对象调用该方法时,该对象都会被加锁。这时候如果需要调用该对象的另一个synchronized方法,则需要在第一个方法调用完毕后再进行,这就实现了最基本的同步。

例1:使用synchronized修饰方法和未修饰方法的区别

(1)使用synchronized修饰过的方法,在多线程执行的过程中,程序依次输出递增3的数字

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Synchronization implements Runnable {
private static int currentCount = 0;
synchronized void printAdd() {
currentCount++;
Thread.yield();
currentCount++;
Thread.yield();
currentCount++;
System.out.println(currentCount);
}
@Override
public void run() {
printAdd();
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
Synchronization test = new Synchronization();
for(int i = 0; i < 100; i++) {
exec.execute(test);
}
exec.shutdown();
}
}

(2)与之相对应的未用synchronized修饰过的方法,在多线程执行的过程中,程序会输出没有规律的数字

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Synchronization implements Runnable {
private static int currentCount = 0;
void printAdd() {
currentCount++;
Thread.yield();
currentCount++;
Thread.yield();
currentCount++;
System.out.println(currentCount);
}
@Override
public void run() {
printAdd();
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
Synchronization test = new Synchronization();
for(int i = 0; i < 100; i++) {
exec.execute(test);
}
exec.shutdown();
}
}

2.synchronized对于类静态成员方法的修饰

与对象相同,Java的每个类也有一个锁,所以我们可以通过将静态方法用synchronized修饰来控制其对于静态共享资源的访问。

三、Lock的使用

在上面的利用synchronized进行同步的描述中,我们都是利用方法所在对象自身的锁来进行同步。除了这种方法之外,我们还可以用Java语言中内置的锁对象来进行显式的加锁。

Lock接口,便是Java语言在java.util.concurrent.locks包中为我们提供的显式锁。目前在该包中有三个Lock的实现(基于JDK 1.7),分别为ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock

Lock对象必须在程序中被显式的创建、锁定和释放。

例3:使用Lock实现多线程之间的同步

 import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; public class LockTest implements Runnable { private static int currentCount = 0;
Lock lock = new ReentrantLock();
void addCount() {
lock.lock();
try {
currentCount++;
Thread.yield();
currentCount++;
Thread.yield();
currentCount++;
System.out.println(currentCount);
} finally {
lock.unlock();
}
}
@Override
public void run() {
addCount();
}
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
LockTest test = new LockTest();
for(int i = 0; i < 100; i++) {
exec.execute(test);
}
exec.shutdown();
}
}

四、synchronized与Lock的对比

在我的理解中,synchronized修饰的方法,在检查到对象已经被加锁的情况后,会等待到该对象锁被释放;之后对对象进行加锁,进行自身方法的执行。

但是Lock则不是如此,Lock可以尝试获取锁一段时间,或者尝试获取锁最后失败,而synchronized方式则不可以。综合来说,采用Lock显式锁可以完成更多并发控制功能,但是其较synchronized麻烦许多,所以根据自身程序的需要可以视情况选择这两种同步方法。

五、总结

本篇文章是简单的介绍了synchronized 及Lock的使用,Lock的高级使用将在下一篇文章进行介绍。小弟才疏学浅,如有错误,请多指出。

Java多线程编程(四)—浅谈synchronized与lock的更多相关文章

  1. Java多线程之线程同步【synchronized、Lock、volatitle】

    线程同步 线程同步:当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多. ...

  2. Java多线程编程实战指南(核心篇)读书笔记(四)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76690961冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  3. Java多线程编程详解

    转自:http://programming.iteye.com/blog/158568 线程的同步 由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Ja ...

  4. Java多线程编程核心技术(三)多线程通信

    线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...

  5. Java多线程编程核心技术(二)对象及变量的并发访问

    本文主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题.阅读本文应该着重掌握如下技术点: synchronized对象监视器为O ...

  6. Java多线程编程核心技术(一)Java多线程技能

    1.进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程. 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位. 举个例子: 打开你的计算机上的任务管 ...

  7. Java多线程编程实战指南(核心篇)读书笔记(五)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76730459冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  8. Java多线程编程实战指南(核心篇)读书笔记(二)

    (尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...

  9. Java多线程学习(二)synchronized关键字(2)

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

随机推荐

  1. Linux之文件过滤分割与合并

    文件过滤分割与合并 1.grep命令 grep(global regular expression print)全面搜索正则表达式并把行打印出来,是一种强大的文本搜索工具,它能使用正则表达式搜索文本, ...

  2. 【Xilinx-Petalinux学习】-00-开始

    基于自己的ZYNQ板子,在上面运行petalinux,已经搞得稳定了,之后详细记录. 现在功能:QSPI启动u-boot和kernel,vdma.tpg.osd.vtc等IP模块在Linux下的驱动, ...

  3. Redis 排行榜 自己简单练习

    <?php class Ranks{ const PREFIX = 'zhengban'; protected $redis = ''; /* 初始化 */ public function __ ...

  4. DevExpress控件学习总结(转)

    DevExpress控件学习总结   1.Navigation & Layout 1.1 Bar Manager 如果想在窗体或用户控件(user control)上添加工具条(bars)或弹 ...

  5. Spring框架中整合JUnit单元测试的方法

    一. 步骤: 1. 拷贝jar包: 1. JUnit-4.9.jar和spring-test-4.2.4.RELEASE.jar ; 2. 替换原来的main函数: 1. 在测试类上使用注解方式替换: ...

  6. inheritprototype原型继承封装及综合继承最简实例

    1.inheritprototype.js ;(function(){    var s = {        inheritObject:function(o){//对象继承封装           ...

  7. STL内存管理

    1. 概述 STL Allocator是STL的内存管理器,也是最低调的部分之一,你可能使用了3年stl,但却不知其为何物. STL标准如下介绍Allocator the STL includes s ...

  8. flex stage.width 与stage.stageWidth的区别

    flex stage.width 与stage.stageWidth的区别: stage.width 是指舞台上的可视对象占据的尺寸 stage.stageWidth是指舞台设置的尺寸,与舞台上的元件 ...

  9. 在DataTable数据类型最后增加一列,列名为“Column”,内容都为“AAA”

    DataTable dt = new DataTable(); dt.Columns.Add("Column", typeof(string)); foreach (DataRow ...

  10. HTML5常用标签分类

    1.行级元素标签:a.span.sup.sub.em.b.big.i.strong 2.块元素标签:div.p.h1~h6.ul.ol.li.table.form.article.footer.hea ...