面试常问synchronized 和 static synchronized区别

面试题:

  • 1、线程1访问AClass.staticWrite()方法时,线程2能访问ACass.staticRead()方法吗?
  • 2、线程1访问new AClass().staticWrite()方法时,线程2能访问new ACass().staticRead()方法吗?
  • 3、线程1访问AClass.staticWrite()方法时,线程2能访问new ACass().staticRead()方法吗?
  • 4、AClass a = new AClass(); 线程1访问a.write()方法时,线程2能访问a.read()方法吗?
  • 5、AClass a = new AClass();AClass b = new AClass(); 线程1访问a.write()方法时,线程2能访问b.read()方法吗?
  • 6、线程1访问AClass.staticWrite()方法时,线程2能访问new AClass().read()方法吗?
  • 7、线程1访问new AClass().staticWrite()方法时,线程2能访问new AClass().read()方法吗?

验证代码:

package com.util.countDownLatch;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.After;
import org.junit.Test;

/**
 * <p>@Desc:synchronized与static synchronized区别</p>
 * 面试问题:
 * 1、线程1访问AClass.staticWrite()方法时,线程2能访问ACass.staticRead()方法吗?
 * 2、线程1访问new AClass().staticWrite()方法时,线程2能访问new ACass().staticRead()方法吗?
 * 3、线程1访问AClass.staticWrite()方法时,线程2能访问new ACass().staticRead()方法吗?
 * 4、AClass a = new AClass(); 
 *    线程1访问a.write()方法时,线程2能访问a.read()方法吗?
 * 5、AClass a = new AClass();AClass b = new AClass();
 *    线程1访问a.write()方法时,线程2能访问b.read()方法吗?
 * 6、线程1访问AClass.staticWrite()方法时,线程2能访问new AClass().read()方法吗?
 * 7、线程1访问new AClass().staticWrite()方法时,线程2能访问new AClass().read()方法吗?
 */
public class TestSynchronizedAndStaticSynchronized {

	// CountDownLatch相当于计数器,当所有都准备好了,再一起执行,模仿真实并发
	CountDownLatch mainLatch = new CountDownLatch(1);// 主线程用于等待子线程达到一定数量后并发执行
	CountDownLatch threadDownLatch = new CountDownLatch(2);// 定义子线程执行次数
	// 线程池
	private final ExecutorService executorService = Executors.newFixedThreadPool(2);

	// 1、线程1访问AClass.staticWrite()方法时,线程2能访问ACass.staticRead()方法吗?(结论:不能)
	@Test
	public void test1() throws InterruptedException {
		executorService.submit(new Task(mainLatch, threadDownLatch,"AClass","static","write", null));
		executorService.submit(new Task(mainLatch, threadDownLatch,"AClass","static","read", null));

		// 子线程达到并发要求,主线程放行,同时子线程执行函数
		mainLatch.countDown();
	}


	// 2、线程1访问new AClass().staticWrite()方法时,线程2能访问new ACass().staticRead()方法吗?(结论:不能)
	@Test
	public void test2() throws InterruptedException {
		executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","static","write", null));
		executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","static","read", null));

		// 子线程达到并发要求,主线程放行,同时子线程执行函数
		mainLatch.countDown();
	}

	// 3、线程1访问AClass.staticWrite()方法时,线程2能访问new ACass().staticRead()方法吗?(结论:不能)
    @Test
    public void test3() throws InterruptedException {
        executorService.submit(new Task(mainLatch, threadDownLatch,"AClass","static","write", null));
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","static","read", null));

        // 子线程达到并发要求,主线程放行,同时子线程执行函数
        mainLatch.countDown();
    }

    // 4、AClass a = new AClass(); 线程1访问a.write()方法时,线程2能访问a.read()方法吗?(结论:不能)
    @Test
    public void test4() throws InterruptedException {
	    AClass a = new AClass();
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","noStatic","write",a));
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","noStatic","read",a));

        // 子线程达到并发要求,主线程放行,同时子线程执行函数
        mainLatch.countDown();
    }

    // 5、AClass a = new AClass();AClass b = new AClass(); 线程1访问a.write()方法时,线程2能访问b.read()方法吗?(结论:能)
    @Test
    public void test5() throws InterruptedException {
        AClass a = new AClass();
        AClass b = new AClass();
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","noStatic","write",a));
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","noStatic","read",b));

        // 子线程达到并发要求,主线程放行,同时子线程执行函数
        mainLatch.countDown();
    }

    // 6、线程1访问AClass.staticWrite()方法时,线程2能访问new AClass().read()方法吗?(结论:能)
    @Test
    public void test6() throws InterruptedException {
        executorService.submit(new Task(mainLatch, threadDownLatch,"AClass","static","write", null));
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","noStatic","read", null));

        // 子线程达到并发要求,主线程放行,同时子线程执行函数
        mainLatch.countDown();
    }

    // 6.1、线程1访问AClass.staticWrite()方法时,线程2能访问AClass.read()方法吗?(结论:能)
    @Test
    public void test6_1() throws InterruptedException {
        executorService.submit(new Task(mainLatch, threadDownLatch,"AClass","static","write", null));
        executorService.submit(new Task(mainLatch, threadDownLatch,"AClass","noStatic","read", null));

        // 子线程达到并发要求,主线程放行,同时子线程执行函数
        mainLatch.countDown();
    }

    // 7、线程1访问new AClass().staticWrite()方法时,线程2能访问new AClass().read()方法吗?(结论:能)
    @Test
    public void test7() throws InterruptedException {
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","static","write", null));
        executorService.submit(new Task(mainLatch, threadDownLatch,"new AClass","noStatic","read", null));

        // 子线程达到并发要求,主线程放行,同时子线程执行函数
        mainLatch.countDown();
    }

	@After
	public void tearDown() {
		try{
			// 等待子线程全部执行完成,在finally中执行收尾工作
			threadDownLatch.await();
		}catch (Exception e) {
			e.printStackTrace();
		}finally{
			System.out.println("执行结束,释放线程池!");
			executorService.shutdown();
		}
	}

}

/**
 * 线程1:write的任务
 */
class Task implements Runnable {
	private CountDownLatch mainLatch;// 主线程的发令枪
	private CountDownLatch countDownLatch;// 子线程的发令枪
	private String isNewObject;// 对象是否是创建
	private String isStaticMethod;// 是否调用静态方法
	private String whichMethod;// 调用的是write方法还是read方法
    private AClass clazz;
	public Task(CountDownLatch mainLatch, CountDownLatch countDownLatch, String isNewObject, String isStaticMethod, String whichMethod, AClass clazz) {
		this.mainLatch = mainLatch;
		this.countDownLatch = countDownLatch;
		this.isNewObject = isNewObject;
		this.isStaticMethod = isStaticMethod;
		this.whichMethod = whichMethod;
		this.clazz = clazz;
	}

	@SuppressWarnings("static-access")
	@Override
	public void run() {
		try {
			//System.out.printf("当前线程%s-------阻塞中--------", Thread.currentThread().getName());
			mainLatch.await();// 一直阻塞当前线程,直到计时器的值为0,保证同时并发
			if("AClass".equals(isNewObject)) {// 工具类调用
				if("static".equals(isStaticMethod)) {// 调用静态synchronized方法
					if("write".equals(whichMethod)) {// 调用write方法
						AClass.staticWrite();
					}else {// 调用read方法
						AClass.staticRead();
					}
				}
			}else {// new对象调用
                if (clazz != null){
                    if("static".equals(isStaticMethod)) {// 调用静态synchronized方法
                        if("write".equals(whichMethod)) {// 调用write方法
                            clazz.staticWrite();
                        }else {// 调用read方法
                            clazz.staticRead();
                        }
                    }else {// 调用非静态synchronized方法
                        if("write".equals(whichMethod)) {// 调用write方法
                            clazz.write();
                        }else {// 调用read方法
                            clazz.read();
                        }
                    }
                }else{
                    if("static".equals(isStaticMethod)) {// 调用静态synchronized方法
                        if("write".equals(whichMethod)) {// 调用write方法
                            new AClass().staticWrite();
                        }else {// 调用read方法
                            new AClass().staticRead();
                        }
                    }else {// 调用非静态synchronized方法
                        if("write".equals(whichMethod)) {// 调用write方法
                            new AClass().write();
                        }else {// 调用read方法
                            new AClass().read();
                        }
                    }
                }
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			countDownLatch.countDown();
		}
	}
}

class AClass {
	public static synchronized void staticWrite() throws InterruptedException {
		System.out.println("public static synchronized void staticWrite is running start");
		Thread.sleep(3000L);
		System.out.println("public static synchronized void staticWrite is running end");
	}

	public static synchronized void staticRead() throws InterruptedException {
		System.out.println("public static synchronized void staticRead is running start");
		Thread.sleep(5000L);
		System.out.println("public static synchronized void staticRead is running end");
	}

	public synchronized void write() throws InterruptedException {
		System.out.println("public synchronized void write is running start");
		Thread.sleep(8000L);
		System.out.println("public synchronized void write is running end");
	}

	public synchronized void read() throws InterruptedException {
		System.out.println("public synchronized void read is running start");
		Thread.sleep(10000L);
		System.out.println("public synchronized void read is running end");
	}

}

概述

通过分析这两个用法的分析,我们可以理解java中锁的概念。一个是实例锁(锁在某一个实例对象上,如果该类是单例,那么该锁也具有全局锁的概念),一个是全局锁(该锁针对的是类,无论实例多少个对象,那么线程都共享该锁)。实例锁对应的就是synchronized关键字,而类锁(全局锁)对应的就是static synchronized(或者是锁在该类的class或者classloader对象上)。

区别

synchronized关键字

synchronized作用是对类的当前实例(对象)加锁。可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁(Java 并发编程)。
synchronized代码块【synchronized(synObject)】使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题(同步对象或类属性),synchronized代码块可以实现只对需要同步的地方进行同步。
与Lock的区别:1. synchronized是Java语言的关键字,因此是内置特性,Lock是一个类(java.util.concurrent.locks包),通过这个类可以实现同步访问;2. synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用。Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

static synchronized:

每个类有一个锁,它可以用来控制对static数据成员的并发访问。访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁。static synchronized控制类的所有实例(对象)的访问(相应代码块)。synchronized相当于 this.synchronized,static synchronized相当于类.synchronized。

上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的(第6个例子)。

结论

A: synchronized static是某个类的范围,synchronized static cSync{}防止多个线程中多个实例同时访问这个 类中的synchronized static 方法。它可以对类的所有对象实例起作用。

B: synchronized 是某实例的范围,synchronized isSync(){}防止多个线程中这一个实例同时访问这个类的synchronized 方法。

其实理解起来就是:
一个锁的是类对象,一个锁的是实例对象。
若类对象被lock,则类对象的所有同步方法全被lock;
若实例对象被lock,则该实例对象的所有同步方法全被lock。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值