java多线程详解(3)-线程的互斥与同步
前言:前一篇文章主要描述了多线程中访成员变量与局部变量问题,我们知道访成员变量有线程安全问题,在多线程程序中
我们可以通过使用synchronized关键字完成线程的同步,能够解决部分线程安全问题
在java中synchronized同步关键字可以使用在静态方法和实例方法中使用,两者的区别在于:
对象锁与类锁
对象锁
当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁。
如果此对象的对象锁已被其他调用者占用,则需要等待此锁被释放
类锁
由上述同步静态方法引申出一个概念,那就是类锁。其实系统中并不存在什么类锁。当一个同步静态方法被调用时,系统获取的其实就是代表该类的类对象的对象锁
在程序中获取类锁
可以尝试用以下方式获取类锁
synchronized (xxx.class) {...}
synchronized (Class.forName("xxx")) {...}
同时获取2类锁
同时获取类锁和对象锁是允许的,并不会产生任何问题,但使用类锁时一定要注意,一旦产生类锁的嵌套获取的话,就会产生死锁,因为每个class在内存中都只能生成一个Class实例对象。
同步静态方法/静态变量互斥体
由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只由一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。一旦一个静态变量被作为synchronized block的mutex。进入此同步区域时,都要先获得此静态变量的对象锁
代码
/**
* 同步代码块与同步实例方法的互斥
*
* @author cary
*/
public class TestSynchronized {
/**
* 同步代码块
*/
public void testBlock() {
synchronized (this) {
int i = ;
while (i-- > ) {
System.out
.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
}
} /**
* 非同步普通方法
*/
public void testNormal() {
int i = ;
while (i-- > ) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
} /**
* 同步实例方法
*/
public synchronized void testMethod() {
int i = ;
while (i-- > ) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
} /**
* 主方法分别调用三个方法
*
* @param args
*/
public static void main(String[] args) {
final TestSynchronized test = new TestSynchronized();
Thread test1 = new Thread(new Runnable() {
public void run() {
test.testBlock();
}
}, "testBlock");
Thread test2 = new Thread(new Runnable() {
public void run() {
test.testMethod();
}
}, "testMethod");
test1.start();
;
test2.start();
test.testNormal();
}
}
执行结果
testBlock : 4
main : 4
testBlock : 3
main : 3
testBlock : 2
main : 2
testBlock : 1
main : 1
testBlock : 0
main : 0
testMethod : 4
testMethod : 3
testMethod : 2
testMethod : 1
testMethod : 0
上述的代码,第一个方法时用了同步代码块的方式进行同步,传入的对象实例是this,表明是当前对象,
当然,如果需要同步其他对象实例,也不可传入其他对象的实例;第二个方法是修饰方法的方式进行同步。
因为第一个同步代码块传入的this,所以两个同步代码所需要获得的对象锁都是同一个对象锁,
下面main方法时分别开启两个线程,分别调用testBlock()和testMethod()方法,那么两个线程都需要获得该对象锁,
另一个线程必须等待。上面也给出了运行的结果可以看到:直到testBlock()线程执行完毕,释放掉锁testMethod线程才开始执行
(两个线程没有穿插执行,证明是互斥的)
对于普通方法
结果输出是交替着进行输出的,这是因为,某个线程得到了对象锁,但是另一个线程还是可以访问没有进行同步的方法或者代码。
进行了同步的方法(加锁方法)和没有进行同步的方法(普通方法)是互不影响的,一个线程进入了同步方法,得到了对象锁,
其他线程还是可以访问那些没有同步的方法(普通方法)
结论:synchronized只是一个内置锁的加锁机制,当某个方法加上synchronized关键字后,就表明要获得该内置锁才能执行,
并不能阻止其他线程访问不需要获得该内置锁的方法
类锁的修饰(静态)方法和代码块:
/**
*
* 类锁与静态方法锁
*
* @author cary
*/
public class TestSynchronized2 {
/**
* 类锁
*/
public void testClassLock() {
synchronized (TestSynchronized2.class) {
int i = ;
while (i-- > ) {
System.out
.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
}
} public static synchronized void testStaticLock() {
int i = ;
while (i-- > ) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
} /**
* 普通方法
*/
public void testNormal() {
int i = ;
while (i-- > ) {
System.out.println("normal-" + Thread.currentThread().getName()
+ " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
} /**
* 静态方法
*/
public void testStaticNormal() {
int i = ;
while (i-- > ) {
System.out.println("static-" + Thread.currentThread().getName()
+ " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
} /**
* 测试synchronized锁的互斥效果
*
* @param args
*/
public static void main(String[] args) {
final TestSynchronized2 test = new TestSynchronized2();
Thread testClass = new Thread(new Runnable() {
public void run() {
test.testClassLock();
}
}, "testClassLock");
Thread testStatic = new Thread(new Runnable() {
public void run() {
TestSynchronized2.testStaticLock();
}
}, "testStaticLock");
/**
* 线程1
*/
testClass.start();
/**
* 线程2
*/
testStatic.start();
/**
* 成员方法
*/
test.testNormal();
/**
* 静态方法
*/
TestSynchronized2.testStaticLock(); }
}
执行结果
testClassLock :
normal-main :
normal-main :
testClassLock :
normal-main :
testClassLock :
testClassLock :
normal-main :
testClassLock :
normal-main :
testStaticLock :
testStaticLock :
testStaticLock :
testStaticLock :
testStaticLock :
main :
main :
main :
main :
main :
类锁和静态方法锁线程是分先后执行的,没有相互交叉,类锁和静态方法锁是互斥的
其实,类锁修饰方法和代码块的效果和对象锁是一样的,因为类锁只是一个抽象出来的概念,
只是为了区别静态方法的特点,因为静态方法是所有对象实例共用的,
所以对应着synchronized修饰的静态方法的锁也是唯一的,所以抽象出来个类锁。
结论:类锁和静态方法锁是互斥的
锁静态方法和普通方法
/**
* 锁普通方法和静态方法。
*
* @author cary
*/
public class TestSynchronized3 {
/**
* 锁普通方法
*/
public synchronized void testNormal() {
int i = ;
while (i-- > ) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
} /**
* 锁静态方法
*/
public static synchronized void testStatic() {
int i = ;
while (i-- > ) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep();
} catch (InterruptedException ie) {
}
}
} /**
* 普通方法和静态方法
*
* @param args
*/
public static void main(String[] args) {
final TestSynchronized test = new TestSynchronized();
Thread test1 = new Thread(new Runnable() {
public void run() {
test.testNormal();
}
}, "testNormal");
Thread test2 = new Thread(new Runnable() {
public void run() {
TestSynchronized3.testStatic();
}
}, "testStatic");
/**
* 启动普通方法线程
*/
test1.start();
/**
* 启动静态方法线程
*/
test2.start(); }
}
执行结果
testNormal :
testStatic :
testNormal :
testStatic :
testNormal :
testStatic :
testStatic :
testNormal :
testNormal :
testStatic :
上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,
这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。
同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。
到这里,对synchronized的用法已经有了一定的了解。这时有一个疑问,既然有了synchronized修饰方法的同步方式,
为什么还需要synchronized修饰同步代码块的方式呢?而这个问题也是synchronized的缺陷所在
synchronized的缺陷:当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,
必须等待或者阻塞,这对高并发的系统是致命的,这很容易导致系统的崩溃。如果某个线程在同步方法里面发生了死循环,
那么它就永远不会释放这个对象锁,那么其他线程就要永远的等待。这是一个致命的问题。
java多线程详解(3)-线程的互斥与同步的更多相关文章
- JAVA多线程提高二:传统线程的互斥与同步&传统线程通信机制
本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备. 一.线程的互斥 为什么会有线程的互斥 ...
- java多线程详解(6)-线程间的通信wait及notify方法
Java多线程间的通信 本文提纲 一. 线程的几种状态 二. 线程间的相互作用 三.实例代码分析 一. 线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种:(1). 产生(New) ...
- java多线程详解(7)-线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...
- Java 多线程详解(四)------生产者和消费者
Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...
- Java 多线程详解(五)------线程的声明周期
Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...
- Java多线程详解(二)
评论区留下邮箱可获得<Java多线程设计模式详解> 转载请指明来源 1)后台线程 后台线程是为其他线程服务的一种线程,像JVM的垃圾回收线程就是一种后台线程.后台线程总是等到非后台线程死亡 ...
- Java多线程详解
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- 原创Java多线程详解(一)
只看书不实践是不行的.来实践一下~~~~~~(引用请指明来源) 先看看百科对多线程的介绍 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的 ...
- java多线程详解(1)-多线程入门
一.多线程的概念 线程概念 线程就是程序中单独顺序的流控制. 线程本身不能运行,它只能用于程序中. 说明:线程是程序内的顺序控制流,只能使用分配给程序的资源和环境. 进程:操作系统中执行的程序 程序是 ...
随机推荐
- Android签名机制
Android APK 签名比对 发布过Android应用的朋友们应该都知道,Android APK的发布是需要签名的.签名机制在Android应用和框架中有着十分重要的作用. 例如,Android系 ...
- python:让源码更安全之将py编译成so
应用场景 Python是一种面向对象的解释型计算机程序设计语言,具有丰富和强大的库,使用其开发产品快速高效. python的解释特性是将py编译为独有的二进制编码pyc文件,然后对pyc中的指令进行解 ...
- 监听JVM的几个命令(可用于linux 本机)
1. jstat 这个命令对于查看Jvm的堆栈信息很有用.能够查看eden,survivor,old,perm等heap的capacity,utility信息 对于查看系统是不是有能存泄漏以及参数设置 ...
- curl post方法
* [curl_post curl post方式请求接口] * @param [type] $url [接口的url] * @param [type] $data [传递的参数] * @return ...
- 最简单的Github入门基础
起因是小伙伴分享给我github上的一个FQ工具,让我看实现过程.于是,就由关键字"github"搜索开始. 一言之,是个开源的SVN.和CVS.SVN类似,但是,里面有千千万万程 ...
- gulp配置文件备份
/** * Created by leyi on 2016/8/25 0025. */ /*********************package.json依赖模块****************** ...
- jquery change dropdownlist selected option
<select name="corporation"> <option value="1">corporation1</optio ...
- Dictionary和Hashtable的一些异同
Dictionary和Hashtable 区别: Dictionary和Hashtable 区别 Dictionary Hashtable 支持范型 不支持 需要自己做线程同步 通过调用 Synch ...
- Git的基础
http://backlogtool.com/git-guide/cn/intro/intro2_3.html
- windows 下 node.js 和 express 的安装
下载 node 下载和安装 下载地址 https://nodejs.org/en/ 下载文件 node-v4.5.0-x64.msi nodejs 安装 express -g 代表全局安装 npm i ...