Synchronized之一:基本使用
目录:
Synchronized作用
1、Synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或者代码块。
2、同步的作用不仅仅是互斥,它的另一个作用就是共享可变性,当某个线程修改了可变数据并释放锁后,其它线程可以获取被修改变量的最新值。如果没有正确的同步,这种修改对其它线程是不可见的。
一、Synchronized的基本使用
Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题。从语法上讲,Synchronized总共有四种不同的同步块:
1.同步的实例方法(锁用的是其实例对象本身。所有的非静态同步方法执行需要顺序执行,即不能并行执行。)
2.同步的静态方法(锁用的是其类对象本身。所有的静态同步方法执行需要顺序执行,即不能并行执行。)
3.实例方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)
4.静态方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)
静态同步方法与非静态同步方法区别:
所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
而所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
同步块:
而对于同步块,由于其锁是可以选择的,所以只有使用同一把锁的同步块之间才有着竞态条件,这就得具体情况具体分析了,但这里有个需要注意的地方,同步块的锁是可以选择的,但是不是可以任意选择的!!!!这里必须要注意一个物理对象和一个引用对象的实例变量之间的区别!使用一个引用对象的实例变量作为锁并不是一个好的选择,因为同步块在执行过程中可能会改变它的值,其中就包括将其设置为null,而对一个null对象加锁会产生异常,并且对不同的对象加锁也违背了同步的初衷!这看起来是很清楚的,但是一个经常发生的错误就是选用了错误的锁对象,因此必须注意:同步是基于实际对象而不是对象引用的!多个变量可以引用同一个对象,变量也可以改变其值从而指向其他的对象,因此,当选择一个对象锁时,我们要根据实际对象而不是其引用来考虑!作为一个原则,不要选择一个可能会在锁的作用域中改变值的实例变量作为锁对象!
1)执行完同步代码块,就会释放锁。(synchronized)
2)在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。(exception)
3)在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进入对象的等待池。(wait)
从上面的三点我就可以看到stop方法释放锁是在第二点的,通过抛出异常来释放锁,通过证明,这种方式是不安全的,不可靠的。
接下来我就通过几个例子程序来说明一下这三种使用方式(为了便于比较,三段代码除了Synchronized的使用方式不同以外,其他基本保持一致)。
1.1、没有同步的情况:
package com.dxz.synchronize;
public class SynchronizedTest {
public void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
}
public void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
}
public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
}
执行结果如下,线程1和线程2同时进入执行状态,线程2执行速度比线程1快,所以线程2先执行完成,这个过程中线程1和线程2是同时执行的。
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
thread1 Method 1 end
1.2、对普通方法同步:
package com.dxz.synchronize;
public class SynchronizedTest2 {
public synchronized void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
}
public synchronized void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
}
public void method3() {
System.out.println(Thread.currentThread().getName() + " Method 3 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 3 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 3 end");
}
public static void main(String[] args) {
//test2demo1();
//test2demo2();
test2demo3();
}
private static void test2demo1() {
final SynchronizedTest2 test = new SynchronizedTest2();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
private static void test2demo2() {
final SynchronizedTest2 test = new SynchronizedTest2();
final SynchronizedTest2 test2 = new SynchronizedTest2();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
}
private static void test2demo3() {
final SynchronizedTest2 test = new SynchronizedTest2();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test.method3();
}
},"thread2").start();
}
}
test2demo1的执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
test2demo2的执行结果:
thread2 Method 2 start
thread2 Method 2 execute
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 end
thread1 Method 1 end
test2demo3的执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 3 start
thread2 Method 3 execute
thread2 Method 3 end
thread1 Method 1 end
2.1、test2demo2测试方法中说明:test和test2属于不同的对象,所以同步互不干扰,线程1和线程2会并行执行。
2.2、通过test2demo1与test2demo3对比说明:同一个类中的所有synchronized修饰的方法是不能同时调用的,也就是说同时只能调用其中一个方法,比如线程1调用method1方法,在method1方法执行完之前,线程2调用method2方法,这个时候线程2就会阻塞,直到线程1调用完method1方法后,线程2才开始执行method2方法。(不仅仅是多个线程调用同一个同步方法)。
2.3、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
3、静态方法(类)同步
package com.dxz.synchronize;
public class SynchronizedTest3 {
public static synchronized void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
}
public static synchronized void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
}
public synchronized void method3() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
}
public static void main(String[] args) {
//test3demo1();
//test3demo2();
test3demo3();
}
private static void test3demo1() {
final SynchronizedTest3 test = new SynchronizedTest3();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
private static void test3demo2() {
final SynchronizedTest3 test = new SynchronizedTest3();
final SynchronizedTest3 test2 = new SynchronizedTest3();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
}
private static void test3demo3() {
final SynchronizedTest3 test = new SynchronizedTest3();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test.method3();
}
},"thread2").start();
}
}
test3demo1执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
test3demo2执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
test3demo3执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
thread1 Method 1 end
3.1、test3demo1与test3demo2对比:对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。
3.2、test3demo2与test3demo3对比说明:实例对象上的锁与类对象的锁是两个,所以可以并行的执行method1和method3。
4、代码块同步
package com.dxz.synchronize;
public class SynchronizedTest4 {
public void method1(){
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
}
public void method2(){
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
}
public static void main(String[] args) {
test4demo1();
}
private static void test4demo1() {
final SynchronizedTest4 test = new SynchronizedTest4();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
private static void test4demo2() {
final SynchronizedTest4 test = new SynchronizedTest4();
final SynchronizedTest4 test2 = new SynchronizedTest4();
new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start();
new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
}
}
test4demo1执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread1 Method 1 end
thread2 Method 2 execute
thread2 Method 2 end
test4demo2执行结果:
thread1 Method 1 start
thread2 Method 2 start
thread2 Method 2 execute
thread1 Method 1 execute
thread2 Method 2 end
thread1 Method 1 end
4.1、虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。
Synchronized之一:基本使用的更多相关文章
- java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...
- 单例模式中用volatile和synchronized来满足双重检查锁机制
背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1 ...
- Thread 学习记录 <1> -- volatile和synchronized
恐怕比较一下volatile和synchronized的不同是最容易解释清楚的.volatile是变量修饰符,而synchronized则作用于一段代码或方法:看如下三句get代码: int i1; ...
- synchronized使用说明
好久没有更新博客了,今天试着用简单的语言把synchronized的使用说清楚. synchronized是什么? synchronized是用来保证在多线程环境下代码同步执行的可重入的互斥锁.所谓互 ...
- 【Java并发系列04】线程锁synchronized和Lock和volatile和Condition
img { border: solid 1px } 一.前言 多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制.这是Java并发编程中必须要理解的一个知识点.其实使用起来还是比 ...
- (转)Lock和synchronized比较详解
今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉,有许多疑问,所有在网上找了很多文章看了一下,总体说的不够详细,重点和焦点问题没有谈到,但这篇文章相当不 ...
- Synchronized同步性与可见性
Synchronized是具有同步性与可见性的,那么什么是同步性与可见性呢? (1)同步性:同步性就是一个事物要么一起成功,要么一起失败,可谓是有福同享有难同当,就像A有10000去银行转5000给身 ...
- 基于synchronized 或 ReadWriteLock实现 简单缓存机制
package cn.xxx.xxx; import java.util.HashMap; import java.util.Map; import java.util.concurrent.lock ...
- 【Java并发编程实战】-----synchronized
在我们的实际应用当中可能经常会遇到这样一个场景:多个线程读或者.写相同的数据,访问相同的文件等等.对于这种情况如果我们不加以控制,是非常容易导致错误的.在java中,为了解决这个问题,引入临界区概念. ...
- Lock、ReentrantLock、synchronized、ReentrantReadWriteLock使用
先来看一段代码,实现如下打印效果: 1 2 A 3 4 B 5 6 C 7 8 D 9 10 E 11 12 F 13 14 G 15 16 H 17 18 I 19 20 J 21 22 K 23 ...
随机推荐
- Ubuntu 使用root登陆界面
打开终端开启root账户 :sudo passwd -u root 输入当前用户的密码 2.为root账户设置密码:sudo passwd root 设置root密码,输入两次 3.进入到/usr/s ...
- 剑指Offer——最长不包含重复字符的子字符串
Solution 动态规划. f(i)表示包含第i个字符的最长子串. 如果第i个字符没在之前出现过,那么f(i) = f(i - 1) + 1 如果第i个字符在之前出现过,这个时候应该分两种情况,假设 ...
- Apache Kafka之设计
转自: http://blog.csdn.net/kevin_hx001/article/details/9413565 http://kafka.apache.org/design.h ...
- 5.scala中的对象
排版乱?请移步原文获得更好的阅读体验 1.单例对象 scala中没有静态类或者静态方法,都是通过object实现的,它表示某个类的单例对象.如object People是class People的单例 ...
- AVL树 - 学习笔记
2017-08-29 14:35:55 writer:pprp AVL树就是带有平衡条件的二叉查找树.每个节点的左子树和右子树高度相差最多为1的二叉查找树 空树的高度定为-1 对树的修正称为旋转 对内 ...
- JavaScript高级程序设计-读书笔记(2)
第6章 面向对象的程序设计 创建对象 1.最简单方式创建Object的实例,如 var person = new Object(); person.name = “Greg”; person.age ...
- centos7安装ambari教程
ambari版本 :2.4.2 (不过各版本安装过程没啥差异) 目录: 为什么要用Ambari 概念概述 版本信息 原理简介 安装 创建集群 创建集群 手动修改配置 NameNode HA 安装Sma ...
- Java循环跳转语句之 break
生活中,我们经常会因为某些原因中断既定的任务安排.如在参加 10000 米长跑时,才跑了 500 米就由于体力不支,需要退出比赛.在 Java 中,我们可以使用 break 语句退出指定的循环,直接执 ...
- 使用jquery.jqprint.js 实现的打印功能,IE9不能进行打印预览、火狐打印空白界面
提示的内容:SCRIPT438: 对象不支持“ExecWB”属性或方法 首先解决IE9不能打印预览的问题: 查找了一大推资料 ,有两种说法:一种是IE的安全性级别太高:一种是需要安装什么 微软we ...
- HDU5137-最短路-删点
How Many Maos Does the Guanxi Worth Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 512000/5 ...