前言:前一篇文章主要描述了多线程中访成员变量与局部变量问题,我们知道访成员变量有线程安全问题,在多线程程序中

我们可以通过使用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)-线程的互斥与同步的更多相关文章

  1. JAVA多线程提高二:传统线程的互斥与同步&传统线程通信机制

    本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备. 一.线程的互斥 为什么会有线程的互斥 ...

  2. java多线程详解(6)-线程间的通信wait及notify方法

    Java多线程间的通信 本文提纲 一. 线程的几种状态 二. 线程间的相互作用 三.实例代码分析 一. 线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种:(1). 产生(New) ...

  3. java多线程详解(7)-线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...

  4. Java 多线程详解(四)------生产者和消费者

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...

  5. Java 多线程详解(五)------线程的声明周期

    Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...

  6. Java多线程详解(二)

    评论区留下邮箱可获得<Java多线程设计模式详解> 转载请指明来源 1)后台线程 后台线程是为其他线程服务的一种线程,像JVM的垃圾回收线程就是一种后台线程.后台线程总是等到非后台线程死亡 ...

  7. Java多线程详解

    Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...

  8. 原创Java多线程详解(一)

    只看书不实践是不行的.来实践一下~~~~~~(引用请指明来源) 先看看百科对多线程的介绍 多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术.具有多线程能力的 ...

  9. java多线程详解(1)-多线程入门

    一.多线程的概念 线程概念 线程就是程序中单独顺序的流控制. 线程本身不能运行,它只能用于程序中. 说明:线程是程序内的顺序控制流,只能使用分配给程序的资源和环境. 进程:操作系统中执行的程序 程序是 ...

随机推荐

  1. Windows Azure初体验

    目前在IT界,云这个概念的第一意思不再是词典里的解释了.不过它们还是有相同点的——也许确实会酝酿出一块大蛋糕,可也是飘在天上,众神分食之,与我等P民无关.所谓云,不过是网络时代发展到一定阶段的必然产物 ...

  2. nodeJs 模块cookie-session api文档中文翻译,偶自己翻译的

    原文英文文档链接点击 说明 简单的 基于cookie的 session中间件 安装 它是一个可以用npm注册的node模块,可以通过npm install命令安装 npm install cookie ...

  3. Nginx配置文件(nginx.conf)配置详解(2)

    Nginx的配置文件nginx.conf配置详解如下: user nginx nginx ; Nginx用户及组:用户 组.window下不指定 worker_processes 8; 工作进程:数目 ...

  4. WKWebView _WebFilterIsActive returning: NO

    写这篇文是因为在UIWebView加载页面的时候有时候会返回: filterBlacklist = ( ); filterWhitelist = ( ); restrictWeb = 1; useCo ...

  5. C#正则提取html图片等

    去除html标记,比较实用,分享给大家. ///   <summary>   ///   去除HTML标记   ///   </summary>   ///   <par ...

  6. zxing--条码图像处理库

    ZXing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库,它包含了联系到其他语言的端口.Zxing可以实现使用手机的内置的摄像头完成条形码的扫描及解码.   该项目可实现的条形码 ...

  7. tp框架总结(三)

    一.跨模块的调用 class IndexAction extends Action{ public function index(){ $user = new UserAction();   // 类 ...

  8. jenkins调度selenium脚本不打开浏览器解决办法

    原文地址: http://blog.csdn.net/achang21/article/details/45096003 The web browser doesn't show while run ...

  9. 安卓冷知识:LayoutParams

    安卓的布局有很多种,每种都有对应的LayoutParams类,那么它们之间到底是什么关系? 为什么在编写Layout的XML文件时,有的layout_前缀属性有用有的没有用? 一句话道出LayoutP ...

  10. spring mvc学习笔记一:hello world

    下面是创建springmvc简单案例的步骤: 项目的目录结构如下: 此案例使用maven构建. 第一步:创建动态web工程,然后项目->configure->convert to mave ...