多线程-synchronized
引言
synchronized是Java线程同步中的一个重要的概念,synchronized是独占锁(互斥锁),同时也是可重入锁(可重入锁一定程度上避免了死锁的问题,内部是关联一个计数器,加一次锁计数器值加一,为零时释放锁),也是一种重量级锁。
synchronized是Java中的关键字,是一种同步锁,修饰的情形有以下几种:
(1)修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个方法的对象;
(2)修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
(3)修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
(4)修饰一个类,其作用的范围是synchronized后面大括号{}括起来的部分,作用的对象是这个类的所有对象。
修饰一个代码块
(1)一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
package com.huawei.thread;
class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": " + (count++));
Thread.sleep(100);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
}
public class Test38 {
public static void main(String[] args) {
SyncThread sync = new SyncThread();
Thread t1 = new Thread(sync, "A");
Thread t2 = new Thread(sync, "B");
t1.start();
t2.start();
}
}
运行截图:

当两个并发线程t1,t2访问同一个对象sync中的synchronized代码块时,在同一个时刻只能有一个线程得到执行,另一个线程受阻塞。t1和t2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
main函数做简单修改:
public static void main(String[] args) {
Thread t1 = new Thread(new SyncThread(), "A");
Thread t2 = new Thread(new SyncThread(), "B");
t1.start();
t2.start();
}

synchronized只锁定对象,每个对象只有一个锁与之相关联。t1和t2分别创建了SyncThread对象与之关联,因此t1和t2锁定的对象是不同的,互不干扰,不形成互斥,所有两个线程可以同时执行。
(2)当一个线程访问对象的一个synchronized同步块时,另一个线程仍然可以访问该对象中的非synchronized不同代码块。
package com.huawei.thread;
class Counter implements Runnable {
private int count;
public Counter() {
count = 0;
}
public void addCount() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": " + (count++));
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public void printCount() {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + " count: " + count);
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
String tName = Thread.currentThread().getName();
if (tName.equals("A")) {
addCount();
} else if (tName.equals("B")) {
printCount();
}
}
}
public class Test39 {
public static void main(String[] args) {
Counter counter = new Counter();
Thread t1 = new Thread(counter, "A");
Thread t2 = new Thread(counter, "B");
t1.start();
t2.start();
}
}
运行截图:

addCount是一个synchronized方法,printCount是非synchronized。从上面的结果中可以看出一个线程访问一个对象的synchronized代码块时,其他线程可以访问对象的非synchronized代码块而不受阻塞。
(3)指定要给某个对象加锁
package com.huawei.thread;
class Account {
private String name;
private float amount;
public Account(String name, float amount) {
this.name = name;
this.amount = amount;
}
public void deposit(float amt) {
amount += amt;
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
public void withdraw(float amt) {
amount -= amt;
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
public float getAmount() {
return amount;
}
@Override
public String toString() {
return "Account [name=" + name + ", amount=" + amount + "]";
}
}
class AccountOperator implements Runnable {
private Account account;
public AccountOperator(Account account) {
this.account = account;
}
@Override
public void run() {
synchronized (account) {
account.deposit(500);
account.withdraw(500);
System.out.println(Thread.currentThread().getName() + ": " + account.getAmount());
}
}
}
public class Test40 {
static final int THREAD_NUM = 5;
public static void main(String[] args) {
Account account = new Account("zhang san", 1000.0f);
AccountOperator ao = new AccountOperator(account);
Thread[] threads = new Thread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++) {
threads[i] = new Thread(ao, "Thread" + i);
threads[i].start();
}
}
}

synchronized给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞。
当有一个明确的对象作为锁时,就可以用类似下面这样的方式写程序:
public void method3(SomeObject obj)
{
//obj 锁定的对象
synchronized(obj) {
// #TODO
}
}
当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:
class Test implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance变量
public void method()
{
synchronized(lock) {
// #TODO 同步代码块
}
} public void run() { }
}
注:零长度的byte数组对象创建起来比任何对象都经济。
synchronized不能锁住基本的类型如:int,long,float等非对象类型
修饰一个方法
synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块时大括号括起来的范围,而修饰方法范围是整个函数。
(1)synchronized关键字不能继承
虽然可以用synchronized来定义方法,但是synchronized不属于方法定义的一部分。因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖这个方法,则在子类中这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。
在子类方法中加上synchronized关键字:
class Parent {
public synchronized void method() { }
}
class Child extends Parent {
public synchronized void method() { }
}
在子类方法中调用父类的同步方法:
class Parent {
public synchronized void method() { }
}
class Child extends Parent {
public void method() { super.method(); }
}
(2)在定义接口方法时不能使用synchronized关键字
(3)构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步
修饰一个静态方法
(1)静态方法属于类而不属于对象。synchronized修饰的静态方法锁定的是这个类的所有对象。
package com.huawei.thread;
public class Test42 {
static class SyncThread implements Runnable {
private static int count;
public SyncThread() {
count = 0;
}
public synchronized static void m() {
for (int i = 0; i < 5; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": " + (count++));
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public synchronized void run() {
m();
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new SyncThread(), "A");
Thread t2 = new Thread(new SyncThread(), "B");
t1.start();
t2.start();
}
}

只要修饰了静态方法,相对于整个类及其产生的对象只有一把锁。
(2)Java类中可以有静态代码块static{},可以在其中设置同步代码块
public class Test40 {
static final Integer THREAD_NUM = 5;
static {
synchronized (THREAD_NUM) {
// #TODO
}
}
}
其中synchronized修饰的必须是static变量或者类
(3)如果一个类中定义了一个synchronized 的 static 函数A,也定义了一个 synchronized 的 instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。B方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
package com.huawei.thread;
public class Test43 {
public static void main(String[] args) {
My my = new My();
Thread t1 = new Thread(my, "A");
Thread t2 = new Thread(my, "B");
t1.start();
t2.start();
}
}
class My implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
if (name.equals("A")) {
test1();
} else if (name.equals("B")) {
test2();
}
}
synchronized void test2() {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(100);
System.out.println("test2");
}
} catch (Exception e) {
e.printStackTrace();
}
}
synchronized static void test1() {
try {
for (int i = 0; i < 5; i++) {
Thread.sleep(100);
System.out.println("test1");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

修饰一个类
class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
效果与修饰一个静态方法类似。
小结
(1)无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
(2)每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
(3)实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
参考资料
http://blog.csdn.net/luoweifu/article/details/46613015
http://www.cnblogs.com/beiyetengqing/p/6213437.html
多线程-synchronized的更多相关文章
- Java 多线程 —— synchronized关键字
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- 多线程-synchronized锁
package 多线程.synchronized锁; /*. * * * * */ public class Sale implements Runnable { ; @Override public ...
- java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...
- Java多线程synchronized同步
非线程安全问题 “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程问题”.也即是说,方法中的变量永远是线程安全的. 如果多个线程共同访问1个对象中的实例变量,则可能线程 ...
- JAVA多线程synchronized详解
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...
- Java对象锁和类锁全面解析(多线程synchronized关键字)
最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不过是别人怎么用就跟着用,并没有搞清楚锁的概念.最近也是遇到一些问题,不搞清楚锁的概念,很容易碰壁,甚至有些时候自己连用没 ...
- java 多线程 synchronized与lock的通信机制等问题,结合相应实例说明
1. 利用多线程实现如下需求: 写两个线程,一个线程打印1~52,另一个线程打印A~Z,打印顺序是12A34B...5152Z: 2. 使用synchronized 实现 public class T ...
- BAT美团滴滴java面试大纲(带答案版)之三:多线程synchronized
继续面试大纲系列文章. 从这一篇开始,我们进入ava编程中的一个重要领域---多线程!多线程就像武学中对的吸星大法,理解透了用好了可以得道成仙,俯瞰芸芸众生:而滥用则会遭其反噬. 在多线程编程中要渡的 ...
- 多线程-synchronized、lock
1.什么时候会出现线程安全问题? 在多线程编程中,可能出现多个线程同时访问同一个资源,可以是:变量.对象.文件.数据库表等.此时就存在一个问题: 每个线程执行过程是不可控的,可能导致最终结果与实际期望 ...
随机推荐
- Oracle Extended Tracing
Definitions A trace file is a file that contains diagnostic data used to investigate problems. Als ...
- JSON Bean 相互转换工具(效率是Gson的两倍以上)
本帖最后由 xuehuayous 于 2015-12-24 08:44 编辑 前几天想封装一个自定义控件,用到Json解析,以前都使用Gson来解析Json数据的,但是想到一个简单的自定义控件就没必要 ...
- [80Sec]深掘XSS漏洞场景之XSS Rootkit
顶80SEC的牛. 深掘XSS漏洞场景之XSS Rootkit[完整修订版] EMail: rayh4c#80sec.com Site: http://www.80sec.com Date: 2011 ...
- Verilog语法
语法子集很小,易用. 模块:module…endmodule 端口:input,output,inout(双向特殊) inout比较难用,有一张真值表,需要大家观察后书写,基本原则就是输入时一定是高阻 ...
- mount命令汇总
(一)挂接命令(mount) 首先,介绍一下挂接(mount)命令的使用方法,mount命令参数非常多,这里主要讲一下今天我们要用到的. 命令格式: mount [-t vfstype] [-o op ...
- 在Oracle 11.2.0.1.0下dbms_stats.gather_table_stats收集直方图不准
SQL> select * from v$version; BANNER ------------------------------------------------------------ ...
- SQL通过身份证获取信息
SELECT t.identity_number '身份证号',SUBSTR(t.identity_number,1,2) AS "省份",SUBSTR(t.identity_nu ...
- 【笔记】探索js 的this 对象 (第二部分)
了解this 对象之后 我们明白了this 对象就是指向调用函数的作用域 那么接下来我们便要清除函数究竟在哪个作用域调用 找到调用的作用域首先要了解一下几点: 1.调用栈: 调用栈就是一系列的函数,表 ...
- (转)NIO 分散和聚集
分散和聚集 概述 分散/聚集 I/O 是使用多个而不是单个缓冲区来保存数据的读写方法. 一个分散的读取就像一个常规通道读取,只不过它是将数据读到一个缓冲区数组中而不是读到单个缓冲区中.同样地,一个聚集 ...
- vi 新建编辑文件时报错 E212 can’t open file for writing
在vi修改防火墙配置时,不能够保存,报E212 can’t open file for writing错误. 网上大概给出了两种答案. 一是权限不够,可以用root权限事实,或者sudo 操作. 二是 ...