同步

同步,字面来看,有点一起工作的意思。但在线程同步中,“同”意为协同、互相配合。

比如:

A、B两个线程,并不是说两个线程必须同时一起工作,而是说互相配合工作,在某个时间可能线程A要等线程B去工作,之后线程A才能继续工作。如果理解不了,可以参考java线程(2)——模拟生产者与消费者中的例子。

思考:

为什么会有线程同步?上面例子中线程A为什么要等B工作之后才能继续工作?

在生产者和消费者的例子中,作为消费者,如果生产者还没生产,他就没办法消费只能等着,否则就会出现问题。同样,生产者也不能无止尽的生产,毕竟篮子的容量是有限的。

如果这个例子看不出来问题的严重性,我们可以想想银行取钱的例子,出现问题这后果就相当严重了。所以,多线程如果使用不当,很容易出现线程不安全的问题。尤其涉及同步问题时,一定要小心使用。

实现方式

主要有两种:synchronized和lock,前者被称为内置锁,后者叫外置锁。

关于这两者的不同之处,介绍之后再比较。

1、 synchronized关键字

在消费者和生产者的例子中,我们使用到了synchronized关键字,将篮子的“取”和“放”都做了限制,来实现线程同步。

2、lock

先来看一下他的接口定义,也是在包java.util.concurrent下。

package java.util.concurrent.locks;

public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
void unlock();
Condition newCondition(); }

在上篇博客中介绍了一个使用synchronized关键字实现打印当前时间的方法,我们这里来修改一下。

   /**
* 获得当前时间
*/
public static void getTime() { lock.lock();
try {
System.out.println("1、进入获取时间方法=====");
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format.format(date);
System.out.println("2、" + Thread.currentThread().getName() + ":"
+ time);
System.out.println("3、获取时间成功=====");
System.out.println();
} finally {
lock.unlock();
}
} //调用
static Lock lock=null;
public static void main(String[] args) {
lock = new ReentrantLock(); for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override
public void run() {
getTime(); } }).start(); }
}

结果同样满足条件:

读写锁

Lock中的另一个亮点就是对读写锁的支持。在读写锁中,把对共享资源的访问者划分为读者和写者,读者进行读访问,写者进行写操作。但是,一个读写锁同时能有一个或多个读者,但不能同时既有读者又有写者。

java中的类为ReadWriteLock,提供了读锁和写锁的方法,返回类型都是Lock

public interface ReadWriteLock {

    Lock readLock();

    Lock writeLock();
}

举个例子:

在例子中,data为所要读写的数据,提供了读和写的方法。

    private Object data = null;// 共享数据
/**
* 读数据
*/
public void get() {
try {
//准备数据
System.out.println(Thread.currentThread().getName()
+ " be ready to read data!");
//sleep一段时间,主要为了更明显的体现出差别,无实际意义
Thread.sleep((long) (Math.random() * 1000));
//打印取出数据
System.out.println(Thread.currentThread().getName()
+ "have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
} /**
* 写数据
*
* @param data
*/
public void put(Object data) {
try {
System.out.println(Thread.currentThread().getName()
+ " be ready to write data!");
Thread.sleep((long) (Math.random() * 1000));
this.data = data; // 给data赋值
System.out.println(Thread.currentThread().getName()
+ " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
} }

执行结果:

从结果上看,当程序刚启动时,多个读写线程同时触发,等待执行。无法实现“多读”,也影响了写的操作。

那么,现在来看下加上读写锁之后的效果。

ReadWriteLock rwl = new ReentrantReadWriteLock();

    /**
* 读数据
*/
public void get(){
rwl.readLock().lock(); //读锁
try {
....略
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock(); //释放读锁
}
}

与上面没有使用读写锁相比,最大的不同在于,他可以实现“多读”,并且“读”与“写”互斥。例如:Thread0,2,4进入之后,可以同时读数据。并没有多条写数据同时写。

点睛之笔

1)Lock与ReadWriteLock

以Lock第一个例子ReentrantLock来说,他实现了标准的互斥操作,有点“独占”的感觉。这种情况下,不允许多读,多写,读写等情况发生。

读写锁ReadWriteLock中,允许多个读线程同时访问一个资源,但不允许多个写线程同时访问。更符合实际操作和需要。

2)Lock与synchronized

synchronized比较武断,只能实现互斥,无法实现读写锁的允许多读操作。在并发量比较小的情况下,他是一个不错的选择。但并发量较大的话,他的性能下降很严重。

Lock锁技术除了第一点介绍的优点外,他还具有可重入性,即可以进行多次加锁,也更具公平性。不过为了避免死锁,需要在finally中释放锁。

java线程(4)——线程同步的锁技术的更多相关文章

  1. Java 虚拟机:互斥同步、锁优化及synchronized和volatile

    互斥同步 互斥同步(Mutual Exclusion & Synchronization)是常见的一种并发正确性保证手段.同步是指子啊多个线程并发访问共享数据时,保证共享数据在同一时刻只能被一 ...

  2. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  3. java 线程Thread 技术--线程状态与同步问题

    线程技术第三篇: 线程的状态: 1. 创建状态: 当用new 操作符创建一个新的线程对象时,该线程就处于创建状态,系统不为它分配资源 2.可运行状态:当线程调用start 方法将为线程分配必须的系统资 ...

  4. Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)

    多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...

  5. JAVA语言规范-线程和锁章节之同步、等待和通知

    JAVA语言规范:线程和锁 1 同步 java编程语言提供了线程间通信的多种机制.这些方法中最基本的是同步化,此方法是使用监视器实现的.JAVA中每个对象与一个监视器相关联,一个线程可以加锁和解锁监视 ...

  6. Java线程:线程的同步与锁

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. public ...

  7. java线程(2)--同步和锁

    参考转载:http://rainyear.iteye.com/blog/1734311 http://turandot.iteye.com/blog/1704027 http://www.cnblog ...

  8. Java多线程-线程的同步与锁

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏.例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. package ...

  9. (转)Java线程:线程的同步与锁

      Java线程:线程的同步与锁       一.同步问题提出   线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Fo ...

随机推荐

  1. 于是他错误的点名开始了(trie树)

    题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛CON900). ...

  2. web前端总结面试问题<CSS&HTML问题>

    一个父元素div,一个未知宽度.高度的子元素div [上下左右居中方法总结] //1.position布局,position设为absolute,其他同情景一 2.display:table 父级元素 ...

  3. mysql-介绍

    1.mysql几个重要的文件 每个数据库新建后,会产生数据库文件夹,在该文件夹下每张表均对应以下三个文件: xx.frm  存放表结构 xx.MYD    存放表数据 xx.MYI 存放表索引 mys ...

  4. 更新Composer依赖报错处理Fatal error: Declaration of Fxp\Composer\AssetPlugin\Repository\AbstractAssetsRe

    更新Composer依赖报错处理 Fatal error: Declaration of Fxp\Composer\AssetPlugin\Repository\AbstractAssetsRe po ...

  5. 用友二次开发之科脉TOT3凭证接口

    按客户的要求,根据科脉导出的数据,开发一个工具,将凭证导入T3 这个科目导出的凭证格式. 选择账套登陆,你没看错,这个是我开发的登陆界面. 选择接口文件. 软件自动进数据分类,你可以看到数据了.但只是 ...

  6. python 装饰器 (多个装饰器装饰一个函数---装饰器前套一个函数)

    #带参数的装饰器 #500个函数 # import time # FLAGE = False # def timmer_out(flag): # def timmer(func): # def inn ...

  7. YUM工具使用

    一.yum命令概述: 1.简介: yum命令时在Fedora和RedHat以及SUSE中基于rpm的软件包管理器,它可以使系统管理人员交互和自动化地更细与管理RPM软件包,能够从指定的服务器自动下载R ...

  8. react ant-design自定义图标

    ant-design给我们提供的图标不够怎么办呢?答案是我们可以自定义图标. 自定义图标也挺简单的,现在图标推荐用svg格式,那么我们就需要制作svg图片. 下面让我们看看如果制作svg图片吧. 1. ...

  9. 查询各科成绩最高和最低的分:以如下形式显示:课程ID,最高分,最低分

    SELECT L.C# As 课程ID,L.score AS 最高分,R.score AS 最低分 FROM SC L ,SC AS R WHERE L.C# = R.C# and L.score = ...

  10. 编译net core时nuget里全部报错,\obj\project.assets.json找不到

    除了Nuget管理设置允许下载缺少的程序包 如果你自己设置的程序包源里有一个源访问不到,则可能出现下面错误,导致所有nuget无法还原. 而且在VS2017里不会出现这个 SDK,特别是你网上下载的其 ...