首页 技术 正文
技术 2022年11月15日
0 收藏 609 点赞 4,487 浏览 5699 个字

Java并发编程:线程的同步

<!–*/
.title { text-align: center; }
.todo { font-family: monospace; color: red; }
.done { color: green; }
.tag { background-color: #eee; font-family: monospace;
padding: 2px; font-size: 80%; font-weight: normal; }
.timestamp { color: #bebebe; }
.timestamp-kwd { color: #5f9ea0; }
.right { margin-left: auto; margin-right: 0px; text-align: right; }
.left { margin-left: 0px; margin-right: auto; text-align: left; }
.center { margin-left: auto; margin-right: auto; text-align: center; }
.underline { text-decoration: underline; }
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
p.verse { margin-left: 3%; }
pre {
border: 1px solid #ccc;
box-shadow: 3px 3px 3px #eee;
padding: 8pt;
font-family: monospace;
overflow: auto;
margin: 1.2em;
}
pre.src {
position: relative;
overflow: visible;
padding-top: 1.2em;
}
pre.src:before {
display: none;
position: absolute;
background-color: white;
top: -10px;
right: 10px;
padding: 3px;
border: 1px solid black;
}
pre.src:hover:before { display: inline;}
pre.src-sh:before { content: ‘sh’; }
pre.src-bash:before { content: ‘sh’; }
pre.src-emacs-lisp:before { content: ‘Emacs Lisp’; }
pre.src-R:before { content: ‘R’; }
pre.src-perl:before { content: ‘Perl’; }
pre.src-java:before { content: ‘Java’; }
pre.src-sql:before { content: ‘SQL’; }

table { border-collapse:collapse; }
caption.t-above { caption-side: top; }
caption.t-bottom { caption-side: bottom; }
td, th { vertical-align:top; }
th.right { text-align: center; }
th.left { text-align: center; }
th.center { text-align: center; }
td.right { text-align: right; }
td.left { text-align: left; }
td.center { text-align: center; }
dt { font-weight: bold; }
.footpara:nth-child(2) { display: inline; }
.footpara { display: block; }
.footdef { margin-bottom: 1em; }
.figure { padding: 1em; }
.figure p { text-align: center; }
.inlinetask {
padding: 10px;
border: 2px solid gray;
margin: 10px;
background: #ffffcc;
}
#org-div-home-and-up
{ text-align: right; font-size: 70%; white-space: nowrap; }
textarea { overflow-x: auto; }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00; }
.org-info-js_info-navigation { border-style: none; }
#org-info-js_console-label
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
.org-info-js_search-highlight
{ background-color: #ffff00; color: #000000; font-weight: bold; }
/*]]>–>*/–>

code {color: #FF0000}
pre.src {background-color: #002b36; color: #839496;}

Java并发编程:线程的同步

Table of Contents

线程的同步是通过锁来实现的。在我们使用Concurrent包的锁之前,我们先来了解Java原生的同步锁。Java的每个对象都有一个锁,这个锁具有排他性。只要任一一个线程获得了锁,其他的线程就不能再获得该锁,只能阻塞排队来获取该对象的锁。有锁的这个对象有个专门的名称叫监视对象,它会记录它所监视的所有线程,谁获得了锁,谁在等待锁。
Java中所有的操作都是通过线程来实现的,即使我们不主动创建线程,也是使用进程自动创建的主线程来完成操作的,与进程一起创建的还有一个垃圾回收线程。
线程获取锁的方式,是通过使用synchronized的修饰符来得到的。synchronized修饰符可以使用在几个不同的地方,但它们的作用都是获得一个对象的锁。
synchronized关键字并不是方法签名的一部分,因此,子类不能继承父类的锁。但子类方法的锁和父类方法的锁是一样的,就是说子类锁定的方法可以调用父类中锁定的方法。但是,内部类的同步锁和其外部类的锁是不一样的,要想中内部类中锁住外部类的方法:

synchronized(OuterClass.this) { /* body */ }

1 synchronized 修饰方法

这种使用是最常见的,当我们不获取锁时:

public class SyncDemo implements Runnable {
private int count = 0;
@Override
public void run() {
count++;
System.out.println(count);
}
public static void main(String[] args) {
SyncDemo runnable = new SyncDemo();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(runnable);
thread.start();
}
}
}
2
4
3
5
6
2
7
8
9
10

可以看到,输出的结果完全是不对的。
那么,我们在方法run()前面加上synchronized修饰,再试一下:

synchronized public void run() {
count++;
System.out.println(count);
}
1
2
3
4
5
6
7
8
9
10

结果就是完全正确的了。
所以,synchronized修饰方法之后,可以让线程获得该对象的锁,这样其他线程想要调用该函数的时候,就需要先获得这个对象的锁,从而达到同步的目的。

2 synchronized 修饰代码块

修饰代码块,运行得到的结果与修饰方法是一样的:

public void run() {
synchronized(this) {
count++;
System.out.println(count);
}
}

如果使用synchronized(Object)这种方式来修饰代码块,可以起到和修饰方法类似的效果。只是这里的对象可以是其他的对象,而不限于当前对象。也就是说可以使用这种方法获得任意对象的锁,只要能把对象传进来。同时,也可以只锁定其中的部分代码,只锁定会使用到共享资源的代码,比如,修改对象的成员变量的代码等。而对其他的代码放开,相当于其他代码是并发运行的,只有锁定的部分代码需要排队运行。

3 synchronized 修饰static方法

当我们使用synchronized修饰方法时,获得的是该对象的锁,那么,当我们修饰static的方法时,获取的是不是还是对象的锁呢?

public class SyncDemo implements Runnable {
private static int count = 0;
synchronized public void instanceMethod() {
count++;
System.out.println("instance: " + count);
}
synchronized public static void staticMethod() {
count++;
System.out.println("static: " + count);
}
@Override
public void run() {
instanceMethod();
staticMethod();
}
public static void main(String[] args) {
SyncDemo runnable = new SyncDemo();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(runnable);
thread.start();
}
}
}

我们用synchronized分别修饰普通instance方法和static方法,然后,运行:

instance: 1
static: 3
instance: 3
static: 4
instance: 5
static: 6
instance: 7
static: 8
instance: 9
static: 10
instance: 11
static: 12
instance: 13
static: 14
instance: 15
static: 16
instance: 17
static: 18
instance: 19
static: 20

我们发现它们锁住的不是同一个对象。那么,当修饰static方法时,锁住的是什么对象呢?当修饰static方法时,所有的类的对象的该方法都是同步的,所以,获取的是class对象的锁。每个类在加载完成之后,都会生成一个.class的类对象。我们用两个例子来分别证明一下。
首先,当修饰static方法时,所有的类的对象的该方法都是同步的:

public void run() {
staticMethod();
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
SyncDemo runnable = new SyncDemo();
Thread thread = new Thread(runnable);
thread.start();
}
}
static: 1
static: 2
static: 3
static: 4
static: 5
static: 6
static: 7
static: 8
static: 9
static: 10

创建不同的对象,然后,调用synchronized修饰的static方法,结果是同步的。
然后,我们再对instance方法中的代码块,通过获取.class对象的锁再来运行:

public class SyncDemo implements Runnable {
private static int count = 0;
public void instanceMethod() {
synchronized(SyncDemo.class) {
count++;
System.out.println("instance: " + count);
}
}
synchronized public static void staticMethod() {
count++;
System.out.println("static: " + count);
}
@Override
public void run() {
instanceMethod();
staticMethod();
}
public static void main(String[] args) {
SyncDemo runnable = new SyncDemo();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(runnable);
thread.start();
}
}
}

注意其中的这里:

synchronized(SyncDemo.class) {
count++;
System.out.println("instance: " + count);
}
instance: 1
instance: 2
static: 3
instance: 4
static: 5
static: 6
instance: 7
static: 8
instance: 9
static: 10
instance: 11
static: 12
instance: 13
static: 14
instance: 15
static: 16
instance: 17
static: 18
instance: 19
static: 20

我们可以看到,两个方法是同步执行的,说明它们获取的是同一个锁,所以,修饰static方法时,获取的是.class锁。在锁定代码块时,我们使用this.getClass()和SyncDemo.class是一样的。

Date: 2017-07-05 21:14

Author: WEN YANG

Created: 2017-07-08 Sat 19:45

Emacs 25.2.1 (Org mode 8.2.10)

Validate

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,492
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,907
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,740
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,493
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,132
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,295