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

我们可以通过使用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. Android签名机制

    Android APK 签名比对 发布过Android应用的朋友们应该都知道,Android APK的发布是需要签名的.签名机制在Android应用和框架中有着十分重要的作用. 例如,Android系 ...

  2. python:让源码更安全之将py编译成so

    应用场景 Python是一种面向对象的解释型计算机程序设计语言,具有丰富和强大的库,使用其开发产品快速高效. python的解释特性是将py编译为独有的二进制编码pyc文件,然后对pyc中的指令进行解 ...

  3. 监听JVM的几个命令(可用于linux 本机)

    1. jstat 这个命令对于查看Jvm的堆栈信息很有用.能够查看eden,survivor,old,perm等heap的capacity,utility信息 对于查看系统是不是有能存泄漏以及参数设置 ...

  4. curl post方法

    * [curl_post curl post方式请求接口] * @param [type] $url [接口的url] * @param [type] $data [传递的参数] * @return ...

  5. 最简单的Github入门基础

    起因是小伙伴分享给我github上的一个FQ工具,让我看实现过程.于是,就由关键字"github"搜索开始. 一言之,是个开源的SVN.和CVS.SVN类似,但是,里面有千千万万程 ...

  6. gulp配置文件备份

    /** * Created by leyi on 2016/8/25 0025. */ /*********************package.json依赖模块****************** ...

  7. jquery change dropdownlist selected option

    <select name="corporation"> <option value="1">corporation1</optio ...

  8. Dictionary和Hashtable的一些异同

    Dictionary和Hashtable 区别: Dictionary和Hashtable 区别 Dictionary Hashtable  支持范型 不支持 需要自己做线程同步 通过调用 Synch ...

  9. Git的基础

    http://backlogtool.com/git-guide/cn/intro/intro2_3.html

  10. windows 下 node.js 和 express 的安装

    下载 node 下载和安装 下载地址 https://nodejs.org/en/ 下载文件 node-v4.5.0-x64.msi nodejs 安装 express -g 代表全局安装 npm i ...