synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
    ———————————————————————————————————————
    修饰一个代码块
    a、一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞.
/**
* @author shuliangzhao
* @Title: SyncThread
* @ProjectName design-parent
* @Description: TODO
* @date 2019/6/17 23:25
*/
public class SyncThread implements Runnable { private static int count; public SyncThread() {
count = 0;
} @Override
public void run() {
synchronized (this) {
try {
for (int i = 0;i<5;i++) {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread = new Thread(syncThread,"Thread1");
Thread thread1 = new Thread(syncThread,"Thread2");
thread.start();
thread1.start();
}
}

运行结果

Thread1:0
Thread1:1
Thread1:2
Thread1:3
Thread1:4
Thread2:5
Thread2:6
Thread2:7
Thread2:8
Thread2:9

当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。

把SyncThread的调用稍微改一下:

        Thread thread = new Thread(new SyncThread(),"Thread1");
Thread thread1 = new Thread(new SyncThread(),"Thread2");
thread.start();
thread1.start();

运行结果

Thread2:0
Thread1:0
Thread1:2
Thread1:3
Thread2:1
Thread2:5
Thread1:4
Thread2:6
Thread1:7
Thread2:8

这时相当于创建了两个SyncThread的对象syncThread1和syncThread2,线程thread1执行的是syncThread1对象中的synchronized代码(run),而线程thread2执行的是syncThread2对象中的synchronized代码(run);我们知道synchronized锁定的是对象,这时会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁是互不干扰的,不形成互斥,所以两个线程可以同时执行。
b、当一个线程访问对象的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该对象中的非synchronized(this)同步代码块

/**
* @author shuliangzhao
* @Title: SyncThread1
* @ProjectName design-parent
* @Description: TODO
* @date 2019/6/17 23:53
*/
public class CountThread implements Runnable{ private static int count; public CountThread() {
count = 0;
} public void count() {
synchronized(this) {
for (int i = 0; i < 5; i ++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} public void print() {
for (int i = 0; i < 5; i ++) {
try {
System.out.println(Thread.currentThread().getName() + ":count:" + (count));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} @Override
public void run() {
String threadName = Thread.currentThread().getName();
if (threadName.equals("A")) {
count();
} else if (threadName.equals("B")) {
print();
}
} public static void main(String[] args) {
CountThread countThread = new CountThread();
Thread thread1 = new Thread(countThread, "A");
Thread thread2 = new Thread(countThread, "B");
thread1.start();
thread2.start();
}
}

上面代码中count是一个synchronized的,print是非synchronized的。从上面的结果中可以看出一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。
c、指定要给某个对象加锁

/**
* @author shuliangzhao
* @Title: Account
* @ProjectName design-parent
* @Description: TODO
* @date 2019/6/18 0:04
*/
public class Account {
String name;
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 (InterruptedException e) {
e.printStackTrace();
}
}
//取钱
public void withdraw(float amt) {
amount -= amt;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
} public float getBalance() {
return amount;
}
} /**
* 账户操作类
*/
class AccountOperator implements Runnable{
private Account account;
public AccountOperator(Account account) {
this.account = account;
} public void run() {
synchronized (account) {
account.deposit(500);
account.withdraw(500);
System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
}
} public static void main(String[] args) {
Account account = new Account("zhang san", 10000.0f);
AccountOperator accountOperator = new AccountOperator(account); final int THREAD_NUM = 5;
Thread threads[] = new Thread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i ++) {
threads[i] = new Thread(accountOperator, "Thread" + i);
threads[i].start();
}
}
}

运行结果

Thread0:10000.0
Thread4:10000.0
Thread3:10000.0
Thread2:10000.0
Thread1:10000.0

在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。
当有一个明确的对象作为锁时,就可以用类似下面这样的方式写程序。

public void method3(SomeObject obj)
{
//obj 锁定的对象
synchronized(obj)
{
// todo
}
}

当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁

 private byte[] lock = new byte[0];  // 特殊的instance变量
public void method()
{
synchronized(lock) {
// todo 同步代码块
}
} public void run() { }

说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

修饰一个方法

Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,public synchronized void method(){//todo}; synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。

public synchronized void run() {
for (int i = 0; i < 5; i ++) {
try {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Synchronized作用于整个方法的写法。
写法一:

public synchronized void method()
{
// todo
}

写法二:

public void method()
{
synchronized(this) {
// todo
}
}

在用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(); }
}

在定义接口方法时不能使用synchronized关键字。
构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步

修饰一个静态的方法

我们知道静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象,把SyncThread改造下:

/**
* @author shuliangzhao
* @Title: SyncThread
* @ProjectName design-parent
* @Description: TODO
* @date 2019/6/17 23:25
*/
public class SyncThread implements Runnable { private static int count; public SyncThread() {
count = 0;
} public static void testSync() {
try {
for (int i = 0;i<5;i++) {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
}
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} @Override
public synchronized void run() {
testSync();
} public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread = new Thread(new SyncThread(),"Thread1");
Thread thread1 = new Thread(new SyncThread(),"Thread2");
thread.start();
thread1.start();
}
}

运行结果

Thread1:0
Thread1:1
Thread1:2
Thread1:3
Thread1:4
Thread2:0
Thread2:5
Thread2:6
Thread2:7
Thread2:8

syncThread1和syncThread2是SyncThread的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁。

修饰一个类

用法

class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
public class SyncThread implements Runnable {

    private static int count;

    public SyncThread() {
count = 0;
} public static void testSync() {
synchronized (SyncThread.class) {
try {
for (int i = 0;i<5;i++) {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
}
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} }
} @Override
public synchronized void run() {
testSync();
} public static void main(String[] args) {
SyncThread syncThread = new SyncThread();
Thread thread = new Thread(new SyncThread(),"Thread1");
Thread thread1 = new Thread(new SyncThread(),"Thread2");
thread.start();
thread1.start();
}
}

运行结果

Thread1:0
Thread1:1
Thread1:2
Thread1:3
Thread1:4
Thread2:5
Thread2:6
Thread2:7
Thread2:8
Thread2:9

A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

Java线程之Synchronized用法的更多相关文章

  1. Java线程之synchronized

    翻译:https://www.journaldev.com/1061/thread-safety-in-java 我们知道线程安全在Java中是一个非常重要的主题,当多个线程操作共享数据时,如果没有任 ...

  2. Java线程之 InterruptedException 异常

    Java线程之 InterruptedException 异常   当一个方法后面声明可能会抛出InterruptedException 异常时,说明该方法是可能会花一点时间,但是可以取消的方法. 抛 ...

  3. Java多线程之synchronized(四)

    前面几章都是在说synchronized用于对象锁,无论是修饰方法也好修饰代码块也好,然而关键字synchronized还可以应用到static静态方法上,如果这样写,那就是对当前的*.java文件所 ...

  4. (二)java多线程之synchronized

    本人邮箱: kco1989@qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco198 ...

  5. java基础---->多线程之synchronized(六)

    这里学习一下java多线程中的关于synchronized的用法.我来不及认真地年轻,待明白过来时,只能选择认真地老去. synchronized的简单实例 一. synchronized在方法上的使 ...

  6. Java多线程之synchronized(五)

    上篇介绍了用synchronized修饰static方式来实现“Class 锁”,今天要介绍另一种实现方式,synchronized(class)代码块,写法不一样但是作用是一样的.下面我附上一段代码 ...

  7. Java多线程之synchronized(三)

    在多线程访问同一个对象中的不同的synchronized方法或synchronized代码块的前提下,也就是“对象监控器”为同一个对象的时候,也就是synchronized的锁为同一把锁的时候,调用的 ...

  8. JAVA多线程之Synchronized关键字--对象锁的特点

    一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...

  9. Java多线程之synchronized及其优化

    Synchronized和同步阻塞synchronized是jvm提供的同步和锁机制,与之对应的是jdk层面的J.U.C提供的基于AbstractQueuedSynchronizer的并发组件.syn ...

随机推荐

  1. [Algorithm] 9. Two Sum

    Description Given an array of integers, return indices of the two numbers such that they add up to a ...

  2. python_ 学习笔记(基础语法)

    python的注释 使用(#)对单行注释 使用('''或者""")多行注释,下面的代码肯定了python的牛逼 print("python是世界上最好的语言吗? ...

  3. 集合类(Collection和Map接口)简介

    集合分为Collection和Map,详细分类如下图所示: 以下是测试验证代码: //HashSet,无序(存取不一致).去重 Set set_H = new HashSet(); set_H.add ...

  4. python3 的 zip

    准备放弃生命中这4个小时,然后翻开了python,人生苦短,音乐和python才味甘 1. zip  可以看到zip两个列表,返回一个元组的列表,但是它是个可迭代的对象,得用list才能调用显示: 2 ...

  5. centos7在grub界面下更改root密码

    想要更改root的密码或者忘记了root的密码的时候可以在grub界面下更改root的密码. 百度了很多内容,更多方法都是适用于centos6及以前版本的,终于找到一个可以的. 1.开机后,在下图界面 ...

  6. Educational Codeforces Round 41 D. Pair Of Lines(961D)

    [题意概述] 给出平面上的10W个点,要求判断这些点能否被两条直线穿过,即一个点至少在一条直线上. [题解] 思路很快可以想到.取3个不共线的点,它们形成一个三角形:如果有解,其中的一条直线一定与三角 ...

  7. 解决maven无法加载本地lib/下的jar包问题(程序包XXX不存在)

    这次一个项目用到maven编译,我在本地开发的时候jar包都是放在WEB-INF/lib目录下,通过 BuildPath将jar包导入,然后用MyEclipse中的:maven package命令打成 ...

  8. 【Codeforces 364A】Matrix

    [链接] 我是链接,点我呀:) [题意] 让你求出b[i][j]=s[i]*s[j]规则构成的矩阵 的所有子矩阵中子矩阵的和为a的子矩阵的个数 [题解] (x,y,z,t) 会发现它的和就是sum(x ...

  9. RMAN RECOVERY

    Data Recovery Advisor The health monitor and the ADR The capabilities and limitations of DRA using t ...

  10. poj 1390 Blocks (记忆化搜索)

    Blocks Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 4318   Accepted: 1745 Descriptio ...