Java并发编程:线程的同步
Java并发编程:线程的同步
*/-->
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
Java并发编程:线程的同步的更多相关文章
- java并发编程 线程基础
java并发编程 线程基础 1. java中的多线程 java是天生多线程的,可以通过启动一个main方法,查看main方法启动的同时有多少线程同时启动 public class OnlyMain { ...
- Java 并发编程 | 线程池详解
原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...
- Java并发编程:线程间通信wait、notify
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- 【Java并发编程二】同步容器和并发容器
一.同步容器 在Java中,同步容器包括两个部分,一个是vector和HashTable,查看vector.HashTable的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并 ...
- Java并发编程:线程和进程的创建(转)
Java并发编程:如何创建线程? 在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程去执行一个子任务.下面先讲述一下Java中的应用程序和进程相关的概念知识, ...
- java并发编程 | 线程详解
个人网站:https://chenmingyu.top/concurrent-thread/ 进程与线程 进程:操作系统在运行一个程序的时候就会为其创建一个进程(比如一个java程序),进程是资源分配 ...
- JAVA 并发编程-线程范围内共享变量(五)
线程范围内共享变量要实现的效果为: 多个对象间共享同一线程内的变量 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsi ...
- Java并发编程——线程安全及解决机制简介
简介: 本文主要介绍了Java多线程环境下,可能会出现的问题(线程不安全)以及相应的解决措施.通过本文,你将学习到如下几块知识: 1. 为什么需要多线程(多线程的优势) 1. 多线程带来的问题—线程安 ...
- Java并发编程——线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...
- Java并发编程3-抽象同步队列AQS详解
AQS是AtractQueuedSynchronizer(队列同步器)的简写,是用来构建锁或其他同步组件的基础框架.主要通过一个int类型的state来表示同步状态,内部有一个FIFO的同步队列来实现 ...
随机推荐
- Spark集成Kafka实时流计算Java案例
package com.test; import java.util.*; import org.apache.spark.SparkConf; import org.apache.spark.Tas ...
- pg_config - 检索已安装版本的 PostgreSQL 的信息
SYNOPSIS pg_config {--bindir | --includedir | --includedir-server | --libdir | --pkglibdir | --confi ...
- OGG复制进程延迟不断增长
1.注意通过进程查找sql_id时,进程号要查询两次 2.杀进程的连接 https://www.cnblogs.com/kerrycode/p/4034231.html 参考资料 1.https:// ...
- window x86编译openssl vs2013
1.下载OpenSSL,http://www.openssl.org/source/(Feb 8 18:29:10 2011 openssl-.0.0d.tar.gz[LATEST] ),winrar ...
- image按钮新增的width属性和height属性
代码实例: test.html <!DOCTYPE html> <html lang="en"> <head> <meta charset ...
- wangeditor 支持上传视频版
1.关于使用哪个富文本编辑器. 简单的要求,不要求发布出来的文章排版要求很高. 可用wangediter.(简单,体积小,不可修改上传图片的尺寸大小) 转载 来源: https://blog.csd ...
- mona!mona!mona!
$ PS: $ 关于\(mona\) 是只很棒的猫啦!想知道的可以自己去看\(persona5\)的游戏流程或者动画版啦. \(PPS:\) 补充一下设定啊,\(mona\)是摩尔加纳(原名)的代号啦 ...
- 手写hashmap算法
/** * 01.自定义一个hashmap * 02.实现put增加键值对,实现key重复时替换key的值 * 03.重写toString方法,方便查看map中的键值对信息 * 04.实现get方法, ...
- Categorical Data
This is an introduction to pandas categorical data type, including a short comparison with R's facto ...
- matplotlib.pyplot 包
import matplotlib.pyplot as plt 图片的打开和保存: from PIL import Image img=Image.open('....') img.save('... ...