环境:
jdk1.8
摘要说明:
上节我们主要介绍了线程的基础概念,启动停止方式,状态及常用方法
多个线程同时进行时,往往需要共享资源,如变量;
本节主要讲述线程间共享的关键词synchronized的使用;
synchronized关键字:代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法:synchronized 方法和 synchronized 块。
常用方式有以下几种:对象锁,类锁,变量锁
步骤:
1.对象锁
对象锁:锁是针对每一个new出来的对象的,如果new出来多个对象则相互之间的锁是不共用的;
实例:
package pers.cc.curriculum1.sys;
import pers.cc.tools.SleepTools;
/**
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: SynInst
* @类描述:【类描述】 synchronized关键字对象锁的用法:对象锁
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日上午10:58:58
*/
public class SynInst {
/**
*
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: InstanceSyn
* @类描述:【类描述】使用对象锁的线程
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日上午11:01:10
*/
private static class InstanceSyn extends Thread {
private SynInst synInst;
public InstanceSyn(SynInst synInst) {
this.synInst = synInst;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" is running..." + synInst);
synInst.instance();
}
}
/**
*
* @方法名:instance
* @方法描述【方法功能描述】锁对象,即需要先new一个SynInst1对象,且只锁住这个对象;若new出多个SynInst1对象,相互之间是不共享锁的
* @修改描述【修改描述】
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日 上午11:11:25
* @修改人:cc
* @修改时间:2019年1月21日 上午11:11:25
*/
private synchronized void instance() {
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()+" is going..." + this.toString());
SleepTools.second(2);
System.out.println(Thread.currentThread().getName()+" ended " + this.toString());
}
public static void main(String[] args) {
SynInst synInst1 = new SynInst();
InstanceSyn instanceSyn = new InstanceSyn(synInst1);
instanceSyn.start();
// 线程instanceSyn和instanceSyn2共用一个对象
InstanceSyn instanceSyn2 = new InstanceSyn(synInst1);
instanceSyn2.start();
// 线程instanceSyn和其他连个线程不共用对象
SynInst synInst2 = new SynInst();
InstanceSyn instanceSyn3 = new InstanceSyn(synInst2);
instanceSyn3.start();
}
}
运行结果:由于Thread0和Thread1公用一个对象,所以被锁住了;而Thread1和他们不是使用同一个对象,所以并行;
Thread-0 is running...pers.cc.curriculum1.sys.SynInst@118f2161
Thread-1 is running...pers.cc.curriculum1.sys.SynInst@118f2161
Thread-2 is running...pers.cc.curriculum1.sys.SynInst@52bd51dc
Thread-2 is going...pers.cc.curriculum1.sys.SynInst@52bd51dc
Thread-0 is going...pers.cc.curriculum1.sys.SynInst@118f2161
Thread-0 ended pers.cc.curriculum1.sys.SynInst@118f2161
Thread-2 ended pers.cc.curriculum1.sys.SynInst@52bd51dc
Thread-1 is going...pers.cc.curriculum1.sys.SynInst@118f2161
Thread-1 ended pers.cc.curriculum1.sys.SynInst@118f2161
锁对象还有另外一种锁定方式:在代码块中锁定对象:
package pers.cc.curriculum1.sys;
import pers.cc.tools.SleepTools;
/**
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: SynInst
* @类描述:【类描述】 synchronized关键字对象锁的用法:对象锁
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日上午10:58:58
*/
public class SynInst {
/**
*
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: InstanceSyn
* @类描述:【类描述】
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午2:42:08
*/
private static class InstanceSyn1 extends Thread {
private SynInst synInst;
public InstanceSyn1(SynInst synInst) {
this.synInst = synInst;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is running..." + synInst);
synInst.instance1();
}
}
/**
*
* @方法名:instance1
* @方法描述【方法功能描述】代码块锁定实例对象
* @修改描述【修改描述】
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日 下午2:39:53
* @修改人:cc
* @修改时间:2019年1月21日 下午2:39:53
*/
private void instance1() {
// 锁的是实例对象
synchronized (this) {
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()
+ " is going..." + this.toString());
SleepTools.second(2);
System.out.println(Thread.currentThread().getName() + " ended "
+ this.toString());
}
}
public static void main(String[] args) {
SynInst synInst11 = new SynInst();
InstanceSyn1 instanceSyn11 = new InstanceSyn1(synInst11);
instanceSyn11.start();
// 线程instanceSyn和instanceSyn2共用一个对象
InstanceSyn1 instanceSyn21 = new InstanceSyn1(synInst11);
instanceSyn21.start();
// 线程instanceSyn和其他连个线程不共用对象
SynInst synInst21 = new SynInst();
InstanceSyn1 instanceSyn31 = new InstanceSyn1(synInst21);
instanceSyn31.start();
}
}
2.类锁
类锁:类锁其实是对象锁的一种特殊形式,加锁方法上加上static之后默认所有的调用线程共用类的class对象
实例:
package pers.cc.curriculum1.sys;
import pers.cc.tools.SleepTools;
/**
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: SynClass
* @类描述:【类描述】synchronized关键字对象锁的用法:类锁,实际上锁的就是类的class对象
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午12:26:37
*/
public class SynClass {
/**
*
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: ClassSyn
* @类描述:【类描述】使用类锁的线程
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午12:24:02
*/
private static class ClassSyn extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is running...");
instanceStatic();
}
}
/**
*
* @方法名:instanceStatic
* @方法描述【方法功能描述】锁类;实际上锁的是类的class对象
* @修改描述【修改描述】
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日 下午12:21:15
* @修改人:cc
* @修改时间:2019年1月21日 下午12:21:15
*/
private static synchronized void instanceStatic() {
SleepTools.second(1);
System.out.println(Thread.currentThread().getName() + " is going...");
SleepTools.second(2);
System.out.println(Thread.currentThread().getName() + " ended ");
}
public static void main(String[] args) {
ClassSyn cls = new ClassSyn();
cls.start();
ClassSyn cls2 = new ClassSyn();
cls2.start();
}
}
运行结果:
Thread-0 is running...
Thread-1 is running...
Thread-0 is going...
Thread-0 ended
Thread-1 is going...
Thread-1 ended
同样可使用代码块对类进行锁定 :
package pers.cc.curriculum1.sys;
import pers.cc.tools.SleepTools;
/**
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: SynClass
* @类描述:【类描述】synchronized关键字对象锁的用法:类锁,实际上锁的就是类的对象
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午12:26:37
*/
public class SynClass {
/**
*
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: ClassSyn
* @类描述:【类描述】使用类锁的线程
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午12:24:02
*/
private static class ClassSyn1 extends Thread {
private SynClass synClass;
public ClassSyn1(SynClass synClass) {
this.synClass = synClass;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is running...");
synClass.instanceStatic1();
}
}
/**
*
* @方法名:instanceStatic1
* @方法描述【方法功能描述】
* @修改描述【修改描述】代码块锁,但代码块锁的是类的对象
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日 下午2:23:52
* @修改人:cc
* @修改时间:2019年1月21日 下午2:23:52
*/
private void instanceStatic1() {
//锁的是类的对象
synchronized (SynClass.class) {
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()
+ " is going...");
SleepTools.second(2);
System.out.println(Thread.currentThread().getName() + " ended ");
}
}
public static void main(String[] args) {
// 测试代码块锁类的实例对象
SynClass synClass=new SynClass();
ClassSyn1 cls11 = new ClassSyn1(synClass);
cls11.start();
SynClass synClass2=new SynClass();
ClassSyn1 cls12 = new ClassSyn1(synClass2);
cls12.start();
}
}
3.变量锁
变量锁:其实质就是锁定任意实例对象;
实例:
package pers.cc.curriculum1.sys;
import pers.cc.tools.SleepTools;
/**
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: SynMethod
* @类描述:【类描述】成员对象锁,锁的是成员的实例对象
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午1:46:50
*/
public class SynMember {
private String str = "cc";
private static String str1 = "cc";
/**
*
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: ClassSyn
* @类描述:【类描述】成员变量的锁
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午12:24:02
*/
private static class MemberSyn extends Thread {
private SynMember synMember;
public MemberSyn(SynMember synMember) {
this.synMember = synMember;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is running...");
synchronized (synMember.getStr()) {
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()
+ " is going...");
SleepTools.second(2);
System.out
.println(Thread.currentThread().getName() + " ended ");
}
}
}
/**
*
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: MemberSyn1
* @类描述:【类描述】局部变量测试
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午3:13:57
*/
private static class MemberSyn1 extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is running...");
String str = "cc";
synchronized (str) {
System.out.println(str);
str = "cccc";
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()
+ " is going...");
SleepTools.second(2);
System.out
.println(Thread.currentThread().getName() + " ended ");
}
}
}
/**
*
* @模块名:study_1_thread_basic
* @包名:pers.cc.curriculum1.sys
* @类名称: MemberSyn2
* @类描述:【类描述】静态变量加锁
* @版本:1.0
* @创建人:cc
* @创建时间:2019年1月21日下午3:46:15
*/
private static class MemberSyn2 extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()
+ " is running...");
synchronized (SynMember.str1) {
// System.out.println(SynMember.str1);
// SynMember.str1="cccc";
SleepTools.second(1);
System.out.println(Thread.currentThread().getName()
+ " is going...");
SleepTools.second(2);
System.out
.println(Thread.currentThread().getName() + " ended ");
}
}
}
/**
* @return the str
*/
public String getStr() {
return this.str;
}
public static void main(String[] args) {
// 成员变量加锁
SynMember synMember = new SynMember();
// 线程一
MemberSyn memberSyn = new MemberSyn(synMember);
memberSyn.start();
// 线程二
MemberSyn memberSyn2 = new MemberSyn(synMember);
memberSyn2.start();
// 线程三
SynMember synMember2 = new SynMember();
MemberSyn memberSyn3 = new MemberSyn(synMember2);
memberSyn3.start();
// 局部变量加锁
// MemberSyn1 memberSyn11 = new MemberSyn1();
// memberSyn11.start();
//
// MemberSyn1 memberSyn12 = new MemberSyn1();
//memberSyn12.start();
// 静态变量加锁
// MemberSyn2 memberSyn21 = new MemberSyn2();
// memberSyn21.start();
//
// MemberSyn2 memberSyn22 = new MemberSyn2();
// memberSyn22.start();
}
}
运行证明:不管是成员变量,静态变量还是局部变量上锁过后只能由一个线程运行代码块
4.总结
在java代码中使用synchronized可是使用在代码块和方法中,根据Synchronized用的位置总结如下:
任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取该对象的监视器才能进入同步块和同步方法,如果没有获取到监视器的线程将会被阻塞在同步块和同步方法的入口处,进入到BLOCKED状态:
下图表现了对象,对象监视器,同步队列以及执行线程状态之间的关系:
该图可以看出,任意线程对Object的访问,首先要获得Object的监视器,如果获取失败,该线程就进入同步状态,线程状态变为BLOCKED,当Object的监视器占有者释放后,在同步队列中得线程就会有机会重新获取该监视器。