了解java中synchronized关键字的用法
- 1.synchronized可修饰方法上
- 2.synchronized修饰于代码块中
- 3.synchronized可修饰静态方法上
- 4.总结
synchronized是java的一个关键字,用于java多线程中的同步.
1.synchronized可修饰方法上,被修饰的方法为同步方法,多个线程访问同一对象的同步方法是同步执行的,即排队执行,哪个线程先执行该方法就持有该方法的所属对象的锁Lock,其他线程就处于等待状态。
2.synchronized可修饰于代码块中,被修饰的代码块为同步代码块,多线程访问时,一个线程访问object的synchronized同步代码块,其他线程仍然可以访问该object对象中非synchronized(this)代码块.
3.synchronized可修饰静态方法上,被修饰的静态方法为同步静态方法,是对当前Class类进行锁定,.Class锁对类的所有实例起作用.多个线程访问该类的静态同步方法是同步执行的.
4.多个线程对共享资源的读写的时候才需要同步化,如果不是共享资源,就没有同步的必要.
1.synchronized可修饰方法上
示例:
public class HasSelfPrivateNum {
private int i;
//修饰在方法上
synchronized public void addI(String username) {
try {
if (username.equals("a")) {
i = 100;
System.out.println("a set over");
Thread.sleep(2000);
} else {
i = 200;
System.out.println(" b set over");
}
System.out.println(username + " i = " + i);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//定义线程A
public class ThreadA extends Thread {
private HasSelfPrivateNum hasSelfPrivateNum;
public ThreadA(HasSelfPrivateNum hasSelfPrivateNum){
this.hasSelfPrivateNum = hasSelfPrivateNum;
}
@Override
public void run() {
super.run();
hasSelfPrivateNum.addI("a");
}
}
//定义线程B
public class ThreadB extends Thread {
private HasSelfPrivateNum hasSelfPrivateNum ;
public ThreadB( HasSelfPrivateNum hasSelfPrivateNum ){
this.hasSelfPrivateNum = hasSelfPrivateNum;
}
@Override
public void run() {
super.run();
hasSelfPrivateNum.addI("b");
}
}
public class Run {
//进行测试
public static void main(String []args){
HasSelfPrivateNum hasSelfPrivateNum = new HasSelfPrivateNum();
ThreadA threadA = new ThreadA(hasSelfPrivateNum,"threadA");
threadA.start();
ThreadB threadB = new ThreadB(hasSelfPrivateNum,"threadB");
threadB.start();
}
}
测试结果:
去掉addI方法的synchronized再执行一遍.
由此可得出结论:
1.多个线程同时访问一个没有同步的方法,如果两个线程同时操作同一个对象的实例变量就会出现非线程安全问题.
2.多个线程访问一个同步的方法时线程一定是安全的.
1.1 如果访问不同对象的同步代码块会发生什么问题呢?
示例:只需要将main方法中增加一个示例对象即可。
public class Run {
//测试
public static void main(String[] args) {
HasSelfPrivateNum hasSelfPrivateNumA = new HasSelfPrivateNum();
HasSelfPrivateNum hasSelfPrivateNumB = new HasSelfPrivateNum();
ThreadA threadA = new ThreadA(hasSelfPrivateNumA);
threadA.start();
ThreadB threadB = new ThreadB(hasSelfPrivateNumB);
threadB.start();
}
}
测试结果:
由此得出结论:
1.关键字synchronized取得的锁是对象锁,而不是把一段代码或者方法当做锁.
2.如果是多个线程访问多个对象,则JVM会创建多个锁.在实际开发中尤其要注意这个点,避免踩坑.
2.synchronized修饰于代码块中
示例:
/**
* synchronized同步代码块的使用.
*/
public class ObjectService {
public void ServiceMethod(){
try {
synchronized (this){
System.out.println(Thread.currentThread().getName() + " :" + "begin time = " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " :" + "end time = " + System.currentTimeMillis());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
//定义一个线程1
public class Thread1 extends Thread {
private ObjectService objectService;
public Thread1(ObjectService objectService){
this.objectService = objectService;
}
@Override
public void run() {
objectService.ServiceMethod();
}
}
//定义一个线程2
public class Thread2 extends Thread {
private ObjectService objectService;
public Thread2(ObjectService objectService){
this.objectService = objectService;
}
@Override
public void run() {
objectService.ServiceMethod();
}
}
public class Run {
//测试
public static void main(String[]args){
ObjectService objectService = new ObjectService();
Thread1 thread1 = new Thread1(objectService);
thread1.setName("a");
thread1.start();
Thread2 thread2 = new Thread2(objectService);
thread2.setName("b");
thread2.start();
}
}
测试结果:
结论:synchronized(this)取得的锁是也是对象锁.
2.1 创建不同的对象,调用方法会产生什么结果呢?
public class Run {
//测试
public static void main(String[] args) {
ObjectService objectService1 = new ObjectService();
ObjectService objectService2 = new ObjectService();
Thread1 thread1 = new Thread1(objectService1);
thread1.setName("a");
thread1.start();
Thread2 thread2 = new Thread2(objectService2);
thread2.setName("b");
thread2.start();
}
}
测试结果:
结论:synchronized(this)当前对象是和synchronized同步方法是一样的,都是取的对象锁,如果不是同一个对象的同步代码块,执行结果是异步的.
2.2 如果synchronized(class)类呢,会产生什么样的结果?
示例:将ObjectService 中的 synchronized (this)改一下即可
public class ObjectService {
public void ServiceMethod() {
try {
synchronized (ObjectService.class) {
System.out
.println(Thread.currentThread().getName() + " :" + "begin time = " + System.currentTimeMillis());
Thread.sleep(2000);
System.out
.println(Thread.currentThread().getName() + " :" + "end time = " + System.currentTimeMillis());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试结果:
结论:
1.synchronized(class)锁持有的是class锁。
2.class锁对类中的所有实例起作用,所以是同步运行.
3.synchronized可修饰静态方法上
示例:
public class Service {
synchronized public static void printA(){
try {
System.out.println("线程名为:"+Thread.currentThread().getName()+
"在"+System.currentTimeMillis()+"进入printA");
Thread.sleep(3000);
System.out.println("线程名为:"+ Thread.currentThread().getName()+
"在"+ System.currentTimeMillis()+ "离开printA");
}catch (Exception e){
e.printStackTrace();
}
}
}
//定义线程A
public class ThreadA implements Runnable {
private Service service;
public ThreadA(Service service){
this.service = service;
}
@Override
public void run() {
service.printA();
}
}
//定义线程B
public class ThreadB implements Runnable {
private Service service;
public ThreadB(Service service){
this.service = service;
}
@Override
public void run() {
service.printA();
}
}
public class Run {
//测试
public static void main(String[] args) {
Service service1 = new Service();
Service service2 = new Service();
ThreadA threadA = new ThreadA(service1);
Thread thread1 = new Thread(threadA);
thread1.setName("A");
thread1.start();
ThreadB threadB = new ThreadB(service2);
Thread thread2 = new Thread(threadB);
thread2.setName("B");
thread2.start();
}
}
测试结果:
结论:
1.虽然是不同的对象,但是在静态synchronized方法中还是同步执行.
2.静态synchronized方法获取的也是class锁,对类的所有实例起作用。
3.1如果调用一个静态同步方法和非静态同步方法会出现什么结果呢?
示例: 只需要在service类中增加一个非静态同步方法,然后线程B调用即可
public class Service {
synchronized public static void printA() {
try {
System.out
.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printA");
Thread.sleep(3000);
System.out
.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printA");
} catch (Exception e) {
e.printStackTrace();
}
}
synchronized public void printB() {
System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入printB");
System.out.println("线程名为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开printB");
}
}
public class ThreadB implements Runnable {
private Service service;
public ThreadB(Service service) {
this.service = service;
}
@Override
public void run() {
service.printB();
}
}
测试结果:
结论:
1.造成异步的原因是持有不同的锁,一个是对象锁,另外一个是class锁
2.因为是不同的锁,所以结果是异步执行的
4.总结
1.关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或者某一段代码块.
2.它包含两个特征,互斥性和可见性,同步synchronized不仅可以解决一个线程看到对象处于不一致状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前的所有修改效果.
3.要牢牢记住共享资源才需要同步,非共享资源没有同步的必要,还会造成额外的开销.
4.对象锁和class锁要分清.