面试题:
- 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。