Java中 synchronized 关键字学习
前言
线程安全是我们并发编程中很重要的,为什么会造成线程安全,主要原因有两点:
修饰一个代码块
/** * 同步线程 * Created by leiqi on 2018/4/27. */ public class SynThread implements Runnable { public SynThread(){ } @Override public void run() { synchronized (this){ for (int i= 0; i< 5;i++){ try { System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } } } } }
/** * 访问同一对象的synchronized()this代码块 */ private static void FirstSynMethond() { SynThread synThread = new SynThread(); Thread thread1 = new Thread(synThread,"SynThread1"); Thread thread2 = new Thread(synThread,"SynThread2"); thread1.start(); thread2.start(); }
我们看到的结果如下: SynThread1:0 SynThread1:1 SynThread1:2 SynThread1:3 SynThread1:4 SynThread2:0 SynThread2:1 SynThread2:2 SynThread2:3 SynThread2:4 Process finished with exit code 0
我们可以看到同时调用两个并发线程对象,同时访问同一对象(SynThread)的synchronized(this)代码块时,只有一个线程能够执行,另一个线程受到阻塞,必须等前面的线程执行完之后后面的线程才能执行。这就相当于给对象加了把锁,当一个线程访问该对象时就把所关闭,不让其他线程访问,当前面线程执行完成之后,再把锁打开。这就解决了我们的并发时的安全问题。 /** * 访问不同对象的synchronized()this代码块 */ private static void FirstSynMethond2() { SynThread synThread1 = new SynThread(); SynThread synThread2 = new SynThread(); Thread thread1 = new Thread(synThread1,"SynThread1"); Thread thread2 = new Thread(synThread2,"SynThread2"); thread1.start(); thread2.start(); }
得到结果如下: SynThread1:0 SynThread2:0 SynThread1:1 SynThread2:1 SynThread1:2 SynThread2:2 SynThread2:3 SynThread1:3 SynThread2:4 SynThread1:4 Process finished with exit code 0
/** * 访问同一对象的synchronized(this)代码块和非synchronized(this)代码块 * Created by leiqi on 2018/4/28. */ public class SynthreadDemo2 implements Runnable { private int count; public SynthreadDemo2(){ count = 0; } public void countAdd(){ synchronized (this){ for (int i=0;i<5;i++){ try { System.out.println("Add count"+":"+(count++)); Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } } } } public void printcount(){ for (int i =0; i<5;i++){ try{ System.out.println("print count"+":"+(count++)); Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } } } @Override public void run() { String threadName = Thread.currentThread().getName(); if (threadName.equals("SynThreadDemo1")){ countAdd(); }else if (threadName.equals("SynThreadDemo2")){ printcount(); } } }
/** * 同时访问同一对象的synchronized(this)代码块和非synchronized(this)代码块 */ private static void SecondSynDemoMehond(){ SynthreadDemo2 synthreadDemo2 = new SynthreadDemo2(); Thread thread1= new Thread(synthreadDemo2,"SynThreadDemo1"); Thread thread2= new Thread(synthreadDemo2,"SynThreadDemo2"); thread1.start(); thread2.start(); }
我们得到结果如下所示: print count:1 Add count:0 print count:2 Add count:3 print count:4 Add count:5 print count:6 Add count:7 print count:8 Add count:9 Process finished with exit code 0
上面的两个代码printcount是非synchronized(this)代码块,countAdd是synchronized(this)代码块,根据运行结果我们可以看到当一个线程访问一个对象的synchronized的代码块时其他线程可以访问该对象的非synchronized代码块。 修饰一个方法
/** * 写法一 */ public synchronized void method(){ //todo } /** * 写法二 */ public void method(){ synchronized (this){ //todo } }
这两种写法,写法一修饰一个方法 写法二修饰后整个方法的代码块,两者都是修饰了整个方法的代码,所以两种写法是等价的。在用synchronized修饰一个方法要注意以下几点: 我们可以使用synchronized关键字来修饰方法,但是synchronized是不属于方法的一部分,所以synchronized是不能被继承的。当我们用synchronized修饰一个方法后,在子类覆盖来这个方法,在子类中这个方法并不是同步的,必须显示的在子类调用方法上加上synchronized关键字才可以。也可以在子类调用父类的方法,两种写法demo如下:
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 (this)来同步代码块。
修饰一个静态方法
synchronized修饰一个静态方法写法如下:
public synchronized static void method() { // todo }
由于静态方法是属于类的而不是属于对象的,所以synchronized修饰静态方法是给这个类的对象加上锁,我们将第一个demo稍加改动:
/** * 同步线程 * Created by leiqi on 2018/4/27. */ public class SynThread implements Runnable { public SynThread(){ } @Override public void run() { method(); } private synchronized static void method(){ for (int i= 0; i< 5;i++){ try { System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } } } }
/** * 访问不同对象的synchronized()this代码块 */ private static void FirstSynMethond2() { SynThread synThread1 = new SynThread(); SynThread synThread2 = new SynThread(); Thread thread1 = new Thread(synThread1,"SynThread1"); Thread thread2 = new Thread(synThread2,"SynThread2"); thread1.start(); thread2.start(); }
而这次我们得到的结果如下:
SynThread1:0 SynThread1:1 SynThread1:2 SynThread1:3 SynThread1:4 SynThread2:0 SynThread2:1 SynThread2:2 SynThread2:3 SynThread2:4 Process finished with exit code 0
为啥这次的结果和上面执行的结果不一样昵?这是因为 synchronized这次修饰的是静态方法,静态方法属于类,所以这次synchronized是给累的对象加锁,故而synThread1和synThread2是同一把锁。 修饰一个类
synchronized还可以用来修饰一个类,写法如下:
class ClassName{ public void method(){ synchronized (ClassName.class){ //todo } } }
我们把上面的demo再做个修改 /** * 同步线程 * Created by leiqi on 2018/4/27. */ public class SynThread implements Runnable { public SynThread(){ } @Override public void run() { method(); } private void method(){ synchronized (SynThread.class){ for (int i= 0; i< 5;i++){ try { System.out.println(Thread.currentThread().getName()+":"+i); Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } } } } }
它的运行效果和上面的结果是一样的,最终效果都是给类的对象加锁,synchronized用于修饰一个类时,是给这个类的所有对象加锁,所有对象都是同同一把锁。
总结
通过上面的学习我们简单总结一下:

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java工程师成神之路
一、基础篇JVM JVM内存结构 堆、栈、方法区、直接内存、堆和栈区别 https://blog.csdn.net/zhangqiluGrubby/article/details/59110906 Java内存模型 内存可见性、重排序、顺序一致性、volatile、锁、final http://www.54tianzhisheng.cn/2018/02/28/Java-Memory-Model/ 垃圾回收 内存分配策略、垃圾收集器(G1)、GC算法、GC参数、对象存活的判定 https://blog.csdn.net/qq_31997407/article/details/79735411 JVM参数及调优 Java对象模型 oop-klass、对象头 https://blog.csdn.net/itachi85/article/details/71159719 HotSpot 即时编译器、编译优化 https://blog.csdn.net/qq_16681169/article/details/72945113 类加载机制 classLoader、类加载过程、双亲委派(破坏双亲委派...
- 下一篇
以太坊开发入门,完整入门篇
一个适合区块链新手的以太坊DApp开发教程: http://xc.hubwiz.com/course/5a952991adb3847553d205d1 一个用区块链、星际文件系统(IPFS)、Node.js和MongoDB来构建电商平台: http://xc.hubwiz.com/course/5abbb7acc02e6b6a59171dd6 收集整理了一些免费区块链、以太坊技术开发相关的文件,有需要的可以下载,文件链接: web3.js API官方文档中文版:https://pan.baidu.com/s/1hOV9hEzi7hFxJCL4LTvC6g 以太坊官方文档中文版 :https://pan.baidu.com/s/1ktODJKLMBmkOsi8MPrpIJA 以太坊白皮书中文版 :https://pan.baidu.com/s/1bzAFnzJ35hlQxJ2J4Oj-Ow Solidity的官方文档中文版 :https://pan.baidu.com/s/18yp9XjEqAHpiFm2ZSCygHw Truffle的官方文档中文版 :https://pan.baidu...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Linux系统CentOS6、CentOS7手动修改IP地址
- Red5直播服务器,属于Java语言的直播服务器
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作