一、线程安全问题

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

class TestSyn {

    public static void main(String[] args) throws Exception {
Resource resource = new Resource();
new Thread() {
@Override
public void run() {
while (true) {
r.sayb();
}
}
}.start();
new Thread() {
@Override
public void run() {
while (true) {
r.sayq();
}
}
}.start();
}
} class Resource {
private String name; public void sayb() {
name = "bbb";
System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
} public void sayq() {
name = "qqqqqq";
System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
}
}
/**
*
Thread-1qqqqqq
Thread-1qqqqqq
Thread-0qqqqqq //其中一段错误信息,Thread-0线程也打印了qqqqqq
Thread-0bbb
Thread-0bbb
*/

问题出现过程:

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

二、synchronized代码块

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

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

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

synchronized标记代码有两种方式:

//synchronized代码块
class Resource {
private String name; public void sayb() {
synchronized (this){
name = "bbb";
System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
}
//...其他代码
} public void sayq() {
synchronized (this){
name = "qqqqqq";
System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
}
//...其他代码
}
}
//synchronized方法
class Resource {
private String name; public synchronized void sayb() {
name = "bbb";
System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
//...其他代码
} public synchronized void sayq() {
name = "qqqqqq";
System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
//...其他代码
}
}

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

两种方法有些不同点:

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

注意:

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

三、monitor与锁的重入

class Test {
public static void main(String[] args) {
syn();
} private static synchronized void syn(){
synchronized (Test.class){
synchronized (Test.class){
synchronized (Test.class){
System.out.println(1);
}
}
}
}
}
  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. CentOS7下源码包方式安装Erlang

    1.官网上下载源码包:OTP 19.1 Source File 2.把源码放在source目录中 , 解压 :tar -zxvf otp_src_19.1.tar.gz [或者 直接下载 rpm包 e ...

  2. Python基础入门-实现计算器多种姿势

    在Python中,虽然定义一个函数只需要def关键字,但是他能实现多种功能和用途,比如今天我们讲解的这几种方式.如何使用函数实现一个计算器的功能呢?当然,实现计算器的方式有很多种,我们举几个比较典型的 ...

  3. wpf path语法

    http://www.cnblogs.com/HQFZ/p/4452548.html WPF系列 Path表示语法详解(Path之Data属性语法)

  4. OkHttp3的简单使用(二)

    OkHttp3的简单封装 public class OkHttpUtil { public static final String TAG="OkHttpUtil"; privat ...

  5. delphi7和XE下 获取路径

    XE里没有Application.Exename所以用paramstr(0); Str:=ParamStr(0); //自己全路径c:\555\qq.exe,可用于控制台Str:=ExpandFile ...

  6. C# 调接口

    上一个项目,需要mvc管理后台调接口项目,以便后期的重构扩展,调研后发现后台用的ajax请求,直接调接口可能会有跨域问题,最终在c#代码中实现了这个需求. 1,Ajax请求后台 将接口所需参数传入 2 ...

  7. C# 继承(3)

    接上章代码: class NameList { public NameList() => Console.WriteLine("这个是NameList的构造函数"); ~Na ...

  8. java 文件硬盘存取 练习

    读写文件操作 对字符流文件读写 1 写文件 FileOutputStream 节点类 负责写字节 OutputStreamWriter 转化类  负责字节到字符转换 BufferedWriter 装饰 ...

  9. kali linux之BurpSuite

    web安全工具中的瑞士军刀,统一的集成工具发现web安全漏洞 所有的工具共享一个能处理并显示http消息的可扩展框架, 模块之间无缝交换信息. 有free版和professional版,java开发, ...

  10. Java面向对象之内部类(访问格式)

    一.基础概念 1.内部类是定义在类中的类.A类需要直接访问B类中的成员时,可以将A类定义到B类中,作为B类的内部类存在. 2.内部类可以相当于外部类中的一个成员,可以被成员修饰符所修饰.(如:publ ...