一、线程安全问题

多线程操作各自线程创建的资源的时候,不存在线程安全问题。但多线程操作同一个资源的时候就会出现线程安全问题。下例为两个线程操作同一个name资源时发生的问题。

  1. class TestSyn {
  2.  
  3. public static void main(String[] args) throws Exception {
  4. Resource resource = new Resource();
  5. new Thread() {
  6. @Override
  7. public void run() {
  8. while (true) {
  9. r.sayb();
  10. }
  11. }
  12. }.start();
  13. new Thread() {
  14. @Override
  15. public void run() {
  16. while (true) {
  17. r.sayq();
  18. }
  19. }
  20. }.start();
  21. }
  22. }
  23.  
  24. class Resource {
  25. private String name;
  26.  
  27. public void sayb() {
  28. name = "bbb";
  29. System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
  30. }
  31.  
  32. public void sayq() {
  33. name = "qqqqqq";
  34. System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
  35. }
  36. }
  37. /**
  38. *
  39. Thread-1qqqqqq
  40. Thread-1qqqqqq
  41. Thread-0qqqqqq //其中一段错误信息,Thread-0线程也打印了qqqqqq
  42. Thread-0bbb
  43. Thread-0bbb
  44. */

问题出现过程:

    1. Thread-0获取执行权执行name="bbb"
    1. Thread-1获得执行权执行name="qqqqqq"
    1. Thread-0重新获得执行权执行打印指令,这时Thread-0就打印出了qqqqqq

二、synchronized代码块

如果name赋值,打印name是一个原子操作就可以避免线程安全问题。

java中synchronized可以标记一段代码,达到原子操作的效果。

  1. 当一个线程执行标记有synchronized代码时将获得该对象的锁,然后开始执行synchronized标记的代码。
  2. 每一个对象只有一个锁,因此其他线程无法获得该对象锁。
  3. 其他线程如果这时候也执行到了标记有synchronized的代码将阻塞,直到获得对象锁的线程执行完synchronized标记的代码。
  4. 然后持有锁的线程释放锁。
  5. 其他线程开始争夺锁,回到第1步。

synchronized标记代码有两种方式:

  1. //synchronized代码块
  2. class Resource {
  3. private String name;
  4.  
  5. public void sayb() {
  6. synchronized (this){
  7. name = "bbb";
  8. System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
  9. }
  10. //...其他代码
  11. }
  12.  
  13. public void sayq() {
  14. synchronized (this){
  15. name = "qqqqqq";
  16. System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
  17. }
  18. //...其他代码
  19. }
  20. }
  1. //synchronized方法
  2. class Resource {
  3. private String name;
  4.  
  5. public synchronized void sayb() {
  6. name = "bbb";
  7. System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
  8. //...其他代码
  9. }
  10.  
  11. public synchronized void sayq() {
  12. name = "qqqqqq";
  13. System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
  14. //...其他代码
  15. }
  16. }

上例中两个线程执行的是同一个对象的方法,因此他们抢夺同一个锁,一个线程执行的时候,另一个线程阻塞。

两种方法有些不同点:

  1. synchronized方法标记在非static方法上,线程获得的锁为this,例子中为resource对象。若标记在static方法上则线程获得的锁为Resource.class对象。
  2. synchronized标记在代码块上,可以由用户自己指定,而且代码块的范围也可以自己指定。因此synchronized代码块比synchronized方法更加灵活。

注意:

  1. 使用synchronized会降低性能,使用时尽量缩小synchronized标记的范围。
  2. synchronized不会死锁,异常抛出时虚拟机会释放锁。

三、monitor与锁的重入

  1. class Test {
  2. public static void main(String[] args) {
  3. syn();
  4. }
  5.  
  6. private static synchronized void syn(){
  7. synchronized (Test.class){
  8. synchronized (Test.class){
  9. synchronized (Test.class){
  10. System.out.println(1);
  11. }
  12. }
  13. }
  14. }
  15. }
  1. 虚拟机为每个对象分配了一个monitor。
  2. 前面说到当一个线程进入synchronized代码块,就获得了对象锁,实际上是monitor的计数器++。
  3. monitor的计数器>0时,线程获得锁,因此上面的代码monitor的计数器每次进入一个synchronized代码块monitor的计数器++,结束一个代码块monitor的计数器--。
  4. monitor的计数器=0时,线程才会释放锁。

这就是锁的重入

java多线程-synchronized的更多相关文章

  1. Java 多线程 —— synchronized关键字

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  2. Java多线程synchronized同步

    非线程安全问题 “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程问题”.也即是说,方法中的变量永远是线程安全的. 如果多个线程共同访问1个对象中的实例变量,则可能线程 ...

  3. JAVA多线程synchronized详解

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...

  4. java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解

    synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...

  5. java 多线程 synchronized与lock的通信机制等问题,结合相应实例说明

    1. 利用多线程实现如下需求: 写两个线程,一个线程打印1~52,另一个线程打印A~Z,打印顺序是12A34B...5152Z: 2. 使用synchronized 实现 public class T ...

  6. Java多线程-synchronized关键字

    进程:是一个正在执行中的程序.每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元. 线程:就是进程中的一个独立的控制单元.线程在控制着进程的执行. 一个进程中至少有一个线程 Ja ...

  7. Java多线程synchronized关键字

    synchronized关键字代表着同步的意思,在Java中被synchronized修饰的有三种情况 1.同步代码块 //锁为objsynchronized(obj){ while(true){ i ...

  8. Java 多线程 - Synchronized关键字

    目录 1-Synchronized 关键字概述 2- Synchronized关键字作用域 3- Synchronized 原理(反编译指令解释) 正文 1-Synchronized 关键字概述 由于 ...

  9. java多线程:synchronized和lock比较浅析

    转载:http://www.toutiao.com/a6392135944652587266/?tt_from=weixin&utm_campaign=client_share&app ...

随机推荐

  1. yum 安装telnet

    检测是否安装 rpm -qa |grep telnet 安装 yum install xinetd yum install telnet-server yum -y install telnet 再次 ...

  2. 编写高质量代码改善C#程序的157个建议——建议16:元素数量可变的情况下不应使用数组

    建议16:元素数量可变的情况下不应使用数组 在C#中,数组一旦被创建,长度就不能改变.如果我们需要一个动态且可变长度的集合,就应该使用ArrayList或List<T>来创建. 而数组本身 ...

  3. 综合学生信息管理系统(JSP+JDBC)

    原创 通过JSP+JDBC制作一个简单的操作数据库中表信息的系统. 总体界面如下,一共有5个功能块. 功能一:列出全部学生 功能二:按条件查询学生 功能三:新添加学生 功能四:按条件删除学生 功能五: ...

  4. android MVP模式简单介绍

    原文 http://zhengxiaopeng.com/2015/02/06/Android%E4%B8%AD%E7%9A%84MVP/ 前言 MVP作为一种MVC的演化版本在Android开发中受到 ...

  5. CSRF漏洞详细说明

    CSRF漏洞详细说明 通常情况下,有三种方法被广泛用来防御CSRF攻击:验证token,验证HTTP请求的Referer,还有验证XMLHttpRequests里的自定义header.鉴于种种原因,这 ...

  6. unix网络编程str_cli使用epoll实现

    unix网络编程str_cli使用epoll实现 unix环境高级编程中也有这个函数,都是为了讲解IO多路转接.从本质上来看epoll就是一个改善了的select和poll,本质没发生任何变化,对于构 ...

  7. 标准模板库使用参考——vector向量容器

    C++的STL从广义上讲分为algorithm(算法),container(容器)和iterator(迭代器)三类,包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法. 在C++标准库中,ST ...

  8. Liunx常用的100条命令汇存

    1.关机 shutdown -h now 立刻关机 poweroff shutdown -r now 立刻重启 reboot logout 注销 2.进入图形界面 startx 3.vi编辑器 [vi ...

  9. 常用跨平台IDE如何添加main函数的参数并正确执行

    1. Eclipse-cdt如何添加main函数参数: 打开eclipse,新建工程.新建源文件,此处以C语言为例,写入代码如下: #include <stdio.h> //int arg ...

  10. error while loading shared libraies :libopencv_core_so.3.4:cannot open shared object

    TX2 上安装自己编译的opencv,使用时出现: error while loading shared libraies :libopencv_core_so.3.4:cannot open sha ...