一、Synchronized概念
其实每个java对象都是可以实现同步的内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。这也是一个互斥锁,同一时间只有一个线程能够获得锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,直到线程B释放这个锁。
对象锁和类锁:对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。每个类只有一个class对象,但是却可以有多个实例对象,所以不同对象实例的对象锁是互不干扰。但是每个类只有一个类锁。其实类锁只是一个抽象的概念,其实只需要理解锁定实例方法和静态方法的区别。
注:Synchronized锁的是一个对象。
二、Synchronized对象锁
1、一个对象多个线程
package com.xiangping.thread.demo;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;
/**
*
* @author 向平
* @version $Id: Test.java, v 0.1 2018年8月10日 上午10:52:45 向平 Exp $
*/
class XPThreadFactory implements ThreadFactory {
/**
* 原子操作保证每个线程都有唯一的
*/
private static final AtomicInteger threadCount = new AtomicInteger(1);
private final AtomicInteger mThreadNum = new AtomicInteger(1);
private final String threadName;
private final boolean daemonThread;
private final ThreadGroup threadGroup;
public XPThreadFactory() {
this("XPThreadFactory-" + threadCount.getAndIncrement(), false);
}
public XPThreadFactory(String prefix) {
this(prefix, false);
}
public XPThreadFactory(String threadName, boolean isDaemon) {
this.threadName = StringUtils.isNotEmpty(threadName) ? threadName+"thread-":"thread-";
daemonThread = isDaemon;
SecurityManager s = System.getSecurityManager();
threadGroup = (s == null) ? Thread.currentThread().getThreadGroup() : s.getThreadGroup();
}
@Override
public Thread newThread(Runnable runnable) {
String name = threadName + mThreadNum.getAndIncrement();
Thread ret = new Thread(threadGroup, runnable, name, 0);
ret.setDaemon(daemonThread);
return ret;
}
}
class XPSynchronized {
public synchronized void getSum() {
System.out.println(Thread.currentThread().getName() + ":getSum start");
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
try {
Thread.sleep(50);
} catch (InterruptedException e) {}
}
System.out.println(Thread.currentThread().getName() + ":getSum end:sum=" + sum);
}
}
public class Test {
public static void main(String[] args) {
XPSynchronized task = new XPSynchronized();
ThreadPoolExecutor threadPool =
new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
new XPThreadFactory("xiangping", false), new AbortPolicy());
threadPool.execute(new Runnable() {
@Override
public void run() {
task.getSum();
}
});
threadPool.execute(new Runnable() {
@Override
public void run() {
task.getSum();
}
});
}
}
执行结果:
xiangpingthread-1:getSum start
xiangpingthread-1:getSum end:sum=45
xiangpingthread-2:getSum start
xiangpingthread-2:getSum end:sum=45
结果分析:我们从执行结果可以看出,是线程1执行完成以后,线程2才执行的,代表线程1获取了锁住了对象(this对象,隐式第一个参数),当线程2去执行的时候,只能等待线程1释放对象才能执行。
2.多个对象,多个线程:(上面有的重复代码就不展示了,看关键的代码)
public class Test {
public static void main(String[] args) {
XPSynchronized task = new XPSynchronized();
XPSynchronized task1 = new XPSynchronized();
ThreadPoolExecutor threadPool =
new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
new XPThreadFactory("xiangping", false), new AbortPolicy());
threadPool.execute(new Runnable() {
@Override
public void run() {
task.getSum();
}
});
threadPool.execute(new Runnable() {
@Override
public void run() {
task1.getSum();
}
});
}
}
执行结果:
xiangpingthread-1:getSum start
xiangpingthread-2:getSum start
xiangpingthread-1:getSum end:sum=45
xiangpingthread-2:getSum end:sum=45
结果分析:synchronized取得的锁都是对象锁,而不是把一段代码或方法(函数)当作锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁,其他线程都只能呈等待状态。但是这有个前提:既然锁叫做对象锁,那么势必和对象相关,所以多个线程访问的必须是同一个对象。
三、synchronized锁重入
class XPSynchronizedLock {
public synchronized void method1() {
System.out.println("XPSynchronizedLock.method1()");
method2();
}
public synchronized void method2() {
System.out.println("XPSynchronizedLock.method2()");
method3();
}
public synchronized void method3() {
System.out.println("XPSynchronizedLock.method3()");
method4();
}
public synchronized void method4() {
System.out.println("XPSynchronizedLock.method3()");
}
}
public class Test {
public static void main(String[] args) {
XPSynchronizedLock task = new XPSynchronizedLock();
ThreadPoolExecutor threadPool =
new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
new XPThreadFactory("xiangping", false), new AbortPolicy());
threadPool.execute(new Runnable() {
@Override
public void run() {
task.method1();
}
});
}
}
执行结果:
XPSynchronizedLock.method1()
XPSynchronizedLock.method2()
XPSynchronizedLock.method3()
XPSynchronizedLock.method3()
结果分析:对象本身是可以再次获取自己的内部锁的,这就叫做“锁重入的机制”。支持在父子类继承。
四、任务异常当前线程会释放锁
class XPSynchronizedException {
public synchronized void except(){
try{
System.out.println(Thread.currentThread().getName()+"start");
long l = Integer.MAX_VALUE;
while (true){
long lo = 10 / l;
l--;
}
} catch (Exception e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
XPSynchronizedException task = new XPSynchronizedException();
ThreadPoolExecutor threadPool =
new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
new XPThreadFactory("xiangping", false), new AbortPolicy());
threadPool.execute(new Runnable() {
@Override
public void run() {
task.except();
}
});
threadPool.execute(new Runnable() {
@Override
public void run() {
task.except();
}
});
}
}
执行结果:
xiangpingthread-1start
java.lang.ArithmeticException: / by zero
xiangpingthread-2start
at com.xiangping.thread.demo.XPSynchronizedException.except(Test.java:78)
at com.xiangping.thread.demo.Test$1.run(Test.java:98)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArithmeticException: / by zero
at com.xiangping.thread.demo.XPSynchronizedException.except(Test.java:78)
at com.xiangping.thread.demo.Test$2.run(Test.java:104)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
结果分析:只有等到第一个线程抛出异常后,第二个线程才可以执行
五、Synchronized同步代码块
class XPSynchronizedBlock {
public void block() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"++++非synchronized修饰 " + i);
}
System.out.println();
synchronized (this) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+"---synchronized修饰 " + i);
}
}
}
}
public class Test {
public static void main(String[] args) {
XPSynchronizedBlock task = new XPSynchronizedBlock();
ThreadPoolExecutor threadPool =
new ThreadPoolExecutor(2, 2, 1000, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
new XPThreadFactory("xiangping", false), new AbortPolicy());
threadPool.execute(new Runnable() {
@Override
public void run() {
task.block();
}
});
threadPool.execute(new Runnable() {
@Override
public void run() {
task.block();
}
});
}
}
执行结果:
xiangpingthread-2++++非synchronized修饰 0
xiangpingthread-1++++非synchronized修饰 0
xiangpingthread-1++++非synchronized修饰 1
xiangpingthread-2++++非synchronized修饰 1
xiangpingthread-1++++非synchronized修饰 2
xiangpingthread-2++++非synchronized修饰 2
xiangpingthread-2++++非synchronized修饰 3
xiangpingthread-1++++非synchronized修饰 3
xiangpingthread-2++++非synchronized修饰 4
xiangpingthread-1++++非synchronized修饰 4
xiangpingthread-2++++非synchronized修饰 5
xiangpingthread-1++++非synchronized修饰 5
xiangpingthread-2++++非synchronized修饰 6
xiangpingthread-1++++非synchronized修饰 6
xiangpingthread-2++++非synchronized修饰 7
xiangpingthread-1++++非synchronized修饰 7
xiangpingthread-1++++非synchronized修饰 8
xiangpingthread-2++++非synchronized修饰 8
xiangpingthread-2++++非synchronized修饰 9
xiangpingthread-1++++非synchronized修饰 9
xiangpingthread-1---synchronized修饰 0
xiangpingthread-1---synchronized修饰 1
xiangpingthread-1---synchronized修饰 2
xiangpingthread-1---synchronized修饰 3
xiangpingthread-1---synchronized修饰 4
xiangpingthread-1---synchronized修饰 5
xiangpingthread-1---synchronized修饰 6
xiangpingthread-1---synchronized修饰 7
xiangpingthread-1---synchronized修饰 8
xiangpingthread-1---synchronized修饰 9
xiangpingthread-2---synchronized修饰 0
xiangpingthread-2---synchronized修饰 1
xiangpingthread-2---synchronized修饰 2
xiangpingthread-2---synchronized修饰 3
xiangpingthread-2---synchronized修饰 4
xiangpingthread-2---synchronized修饰 5
xiangpingthread-2---synchronized修饰 6
xiangpingthread-2---synchronized修饰 7
xiangpingthread-2---synchronized修饰 8
xiangpingthread-2---synchronized修饰 9
结果分析:当第一个线程访问对象的synchronized代码块的时候,第二个线程依然可以访问对象方法中其余非synchronized块的部分,当第一个线程进入对象的synchronized代码块的时候,第二个线程如果要访问这段synchronized块,那么访问将会被阻塞。