一、共享资源竞争问题

在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. C语言-for循环

    for循环是C语言中的循环语句之一,它的一般形式为for(初值,条件表达式,步长){语句};初值通常是一个赋值语句, 它用来给循环控制变量赋初值: 条件表达式是一个关系表达式, 它决定什么时候退出循环 ...

  2. java系列--并发

    1.Executor 原博:http://blog.csdn.net/linghu_java/article/details/17123057 2.CountDownLatch()方法 浅析Java中 ...

  3. IOC容器Unity的使用及独立配置文件Unity.Config

    [本段摘录自:IOC容器Unity 使用http://blog.csdn.net/gdjlc/article/details/8695266] 面向接口实现有很多好处,可以提供不同灵活的子类实现,增加 ...

  4. python 日期格式化常用标记

    符号   说明             例子    %a  英文星期的简写 Mon  %A  英文星期的完整编写 Monday  %b  英文月份的简写 Jun  %B  英文月份的完整编写 June ...

  5. js dorado

    1.dorado http://bsdn.org/projects/dorado7/deploy/sample-center/com.bstek.dorado.sample.Main.d#40350

  6. 查找子字符串----KMP算法深入剖析

    假设主串:a b a b c a b c a c b a b      子串:a b c a c 1.一般匹配算法 逐个字符的比较,匹配过程如下: 第一趟匹配 a b a b c a b c a c ...

  7. Oracle中 Instr 这个函数

    http://www.jb51.net/article/42369.htm sql :charindex('字符串',字段)>0 charindex('administrator',MUserI ...

  8. springmvc java.lang.NoSuchMethodError: com.fasterxml.jackson.core.JsonFactory.requiresPropertyOrdering()Z

    在hibernate spring springMVC整合的时候出现下面的情况: WARNING: Exception encountered during context initializatio ...

  9. CodeForces 429B

    Working out Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u   Desc ...

  10. HDU 2568[前进]模拟

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2568 关键思想:傻傻地模拟 代码如下: #include<iostream> using ...