博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java多线程-synchronized
阅读量:6223 次
发布时间:2019-06-21

本文共 3154 字,大约阅读时间需要 10 分钟。

一、线程安全问题

多线程操作各自线程创建的资源的时候,不存在线程安全问题。但多线程操作同一个资源的时候就会出现线程安全问题。下例为两个线程操作同一个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时,线程才会释放锁。

这就是锁的重入

转载于:https://www.cnblogs.com/liuboyuan/p/10083807.html

你可能感兴趣的文章
LVM(Logical Volume Manager)逻辑卷管理 - 简介、历史、术语、安装、命令、实例、故障排除...
查看>>
多选按钮(CheckBox)——Mars Android开发视频教程之第一季第九集(重)
查看>>
订单号消费码生成(线性同余算法)
查看>>
《SQLSERVER2012之T-SQL教程》T-SQL子查询
查看>>
[deviceone开发]-优惠券商户管理端App开源
查看>>
360搜索“搏杀”成功,赢在创新
查看>>
Apache服务出现Forbidden 403的问题总结
查看>>
onSaveInstanceState和onRestoreInstanceState触发的时机
查看>>
Gradle依赖
查看>>
图解Tomcat类加载机制(阿里面试题)
查看>>
在 CentOS Linux 上安装 Cobbler 批量部署系统
查看>>
Jmeter简单应用1
查看>>
Shell之结构判断
查看>>
PULL解析器
查看>>
Bash的那点事
查看>>
易数一键还原(免费的系统备份与还原软件)------创建命令行工具
查看>>
Haproxy+多台MySQL从服务器(Slave) 实现负载均衡
查看>>
CSS3的转换
查看>>
头文件string与string.h的区别
查看>>
我的友情链接
查看>>