1. synchronized方法的弊端
用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间,在这种情况下可以使用synchronized同步语句块来解决。
这句话好理解,demo 略,
2. 用同步代码块解决同步方法的弊端
public class CommonUtils {
public static long beginTime1;
public static long endTime1;
public static long beginTime2;
public static long endTime2;
}
public class Task {
private String getData1;
private String getData2;
public void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
String privateGetData1 = "长时间处理任务后的返回的值 1 threadName="
+ Thread.currentThread().getName();
String privateGetData2 = "长时间处理任务后的返回的值 2 threadName="
+ Thread.currentThread().getName();
synchronized (this) {
getData1 = privateGetData1;
getData2 = privateGetData2;
}
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class MyThread1 extends Thread {
private Task task;
public MyThread1(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
CommonUtils.beginTime1 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime1 = System.currentTimeMillis();
}
}
public class MyThread2 extends Thread {
private Task task;
public MyThread2(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
CommonUtils.beginTime2 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime2 = System.currentTimeMillis();
}
}
public class Run {
public static void main(String[] args) {
Task task = new Task();
MyThread1 thread1 = new MyThread1(task);
thread1.start();
MyThread2 thread2 = new MyThread2(task);
thread2.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long beginTime = CommonUtils.beginTime1;
if (CommonUtils.beginTime2 < CommonUtils.beginTime1) {
beginTime = CommonUtils.beginTime2;
}
long endTime = CommonUtils.endTime1;
if (CommonUtils.endTime2 > CommonUtils.endTime1) {
endTime = CommonUtils.endTime2;
}
System.out.println("耗时: " + ((endTime - beginTime) / 1000));
}
}
运行效果:
begin task
begin task
长时间处理任务后的返回的值 1 threadName=Thread-0
长时间处理任务后的返回的值 2 threadName=Thread-0
end task
长时间处理任务后的返回的值 1 threadName=Thread-1
长时间处理任务后的返回的值 2 threadName=Thread-1
end task
耗时: 3
通过运行结果可以看出耗时变成了3S. 当一个线程访问object的一个snychronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchronized(this)同步代码块.
3. 多个sync代码块间的同步性
public class ObjectService {
public void serviceMethodA() {
try {
synchronized (this) {
System.out.println("A begin time=" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("A end end=" + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void serviceMethodB() {
synchronized (this) {
System.out.println("B begin time=" + System.currentTimeMillis());
System.out.println("B end end=" + System.currentTimeMillis());
}
}
}
public class ThreadA extends Thread {
private ObjectService service;
public ThreadA(ObjectService service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.serviceMethodA();
}
}
public class ThreadB extends Thread {
private ObjectService service;
public ThreadB(ObjectService service) {
super();
this.service = service;
}
@Override
public void run() {
super.run();
service.serviceMethodB();
}
}
public class Run {
public static void main(String[] args) {
ObjectService service = new ObjectService();
ThreadA a = new ThreadA(service);
a.setName("a");
a.start();
ThreadB b = new ThreadB(service);
b.setName("b");
b.start();
}
}
运行结果:
A begin time=1521628118097
A end end=1521628120097
B begin time=1521628120098
B end end=1521628120098
同步运行,另一个被阻塞
当一个线程访问一个对象的sync(this)同步代码块时,其他线程对同一个对象中的其他sync同步代码块的访问将被阻塞.这说明sync使用的是对象监视器
4. 将任意对象作为对象监视器
多个线程调用同一个对象的不同名称的sync同步方法或sync(this)同步代码块时,调用的效果就是按顺序执行,也就是同步,阻塞的。
sync同步方法或sync(this)同步代码块分别有两种作用:
- sync同步方法
- 对其他sync同步方法或sync(this)同步代码块调用呈阻塞状态。
- 同一时间只有一个线程可以执行sync同步方法中的代码。
- sync(this)同步代码块
- 对其他sync同步方法或sync(this)同步代码块调用呈阻塞状态。
- 同一时间只有一个线程可以执行sync(this)同步代码块中的代码。
根据前面对sync(this)同步代码块的作用总结可知,sycn(非this对象)格式的作用只有一种:sync(非this对象x)同步代码块:
- 当多个线程持有对象监视器为同一个对象的前提下,同一时间只有一个线程可以执行sync(this对象x)同步代码块中的代码
- 当持有对象监视器为同一个对象的前提下,同一时间只有一个线程可以执行sync(非this对象x)同步代码块中的代码
5. 细化验证3个结论
sync(非this对象x)格式的写法是将x对象本身作为 对象监视器 这样就可以得出以下3个结论:
- 当多个线程同时执行sync(x){}同步代码块时呈同步效果
- 当其他线程执行x对象中的sync同步方法时呈同步效果
- 当其他线程执行x对象方法里面的sync(this)代码块时也呈现同步效果
(1) 验证第一个结论
当多个线程同时执行sync(x){}同步代码块时呈同步效果
public class MyObject {
}
public class ThreadA extends Thread {
private Service service;
private MyObject object;
public ThreadA(Service service, MyObject object) {
super();
this.service = service;
this.object = object;
}
@Override
public void run() {
super.run();
service.testMethod1(object);
}
}
public class ThreadB extends Thread {
private Service service;
private MyObject object;
public ThreadB(Service service, MyObject object) {
super();
this.service = service;
this.object = object;
}
@Override
public void run() {
super.run();
service.testMethod1(object);
}
}
public class Run1_1 {
public static void main(String[] args) {
Service service = new Service();
MyObject object = new MyObject();
ThreadA a = new ThreadA(service, object);
a.setName("a");
a.start();
ThreadB b = new ThreadB(service, object);
b.setName("b");
b.start();
}
}
public class Service {
public void testMethod1(MyObject object) {
synchronized (object) {
try {
System.out.println("testMethod1 ____getLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("testMethod1 releaseLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行效果:
testMethod1 ____getLock time=1523523486828 run ThreadName=a
testMethod1 releaseLock time=1523523488828 run ThreadName=a
testMethod1 ____getLock time=1523523488829 run ThreadName=b
testMethod1 releaseLock time=1523523490829 run ThreadName=b
由结果可看到同一个对象监视器, 结果同步输出
换不同的对象监视器
public class Run1_2 {
public static void main(String[] args) {
Service service = new Service();
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
ThreadA a = new ThreadA(service, object1);
a.setName("a");
a.start();
ThreadB b = new ThreadB(service, object2);
b.setName("b");
b.start();
}
}
运行结果如下:
testMethod1 ____getLock time=1523523808562 run ThreadName=a
testMethod1 ____getLock time=1523523808564 run ThreadName=b
testMethod1 releaseLock time=1523523810582 run ThreadName=a
testMethod1 releaseLock time=1523523810637 run ThreadName=b
结果异步输出
(2) 验证第二个结论
当其他线程执行x对象中的sync同步方法时呈同步效果
public class MyObject {
synchronized public void speedPrintString() {
System.out.println("speedPrintString ____getLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
System.out.println("-----------------");
System.out.println("speedPrintString releaseLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
}
}
public class ThreadA extends Thread {
private Service service;
private MyObject object;
public ThreadA(Service service, MyObject object) {
super();
this.service = service;
this.object = object;
}
@Override
public void run() {
super.run();
service.testMethod1(object);
}
}
public class ThreadB extends Thread {
private MyObject object;
public ThreadB(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
object.speedPrintString();
}
}
public class Service {
public void testMethod1(MyObject object) {
synchronized (object) {
try {
System.out.println("testMethod1 ____getLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testMethod1 releaseLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
MyObject object = new MyObject();
ThreadA a = new ThreadA(service, object);
a.setName("a");
a.start();
Thread.sleep(100);
ThreadB b = new ThreadB(object);
b.setName("b");
b.start();
}
}
运行结果如下:
testMethod1 ____getLock time=1523524079664 run ThreadName=a
testMethod1 releaseLock time=1523524084664 run ThreadName=a
speedPrintString ____getLock time=1523524084664 run ThreadName=b
-----------------
speedPrintString releaseLock time=1523524084664 run ThreadName=b
(3) 验证第3个结论
当其他线程执行x对象方法里面的sync(this)代码块时也呈现同步效果
只需要把上述项目中的MyObject替换一下即可
public class MyObject {
public void speedPrintString() {
synchronized (this) {
System.out.println("speedPrintString ____getLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
System.out.println("-----------------");
System.out.println("speedPrintString releaseLock time="
+ System.currentTimeMillis() + " run ThreadName="
+ Thread.currentThread().getName());
}
}
}
运行结果如下:
testMethod1 ____getLock time=1523524445528 run ThreadName=a
testMethod1 releaseLock time=1523524450529 run ThreadName=a
speedPrintString ____getLock time=1523524450529 run ThreadName=b
-----------------
speedPrintString releaseLock time=1523524450529 run ThreadName=b
同步输出
6. 静态同步sync方法与sync(class)代码块
(1) 静态同步方法sync
关键字sync还可以应用在static静态方法上,如果这样写,那就是对当前*.java文件对应的Class类进行持锁
public class ThreadA extends Thread {
@Override
public void run() {
Service.printA();
}
}
public class ThreadB extends Thread {
@Override
public void run() {
Service.printB();
}
}
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 (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public static void printB() {
System.out.println("线程名称为: " + Thread.currentThread().getName() + " 在 "
+ System.currentTimeMillis() + "进入printB");
System.out.println("线程名称为: " + Thread.currentThread().getName() + " 在 "
+ System.currentTimeMillis() + "离开printB");
}
}
public class Run {
public static void main(String[] args) {
ThreadA a = new ThreadA();
a.setName("A");
a.start();
ThreadB b = new ThreadB();
b.setName("B");
b.start();
}
}
运行结果如下:
线程名称为: A 在 1523525481258 进入printA
线程名称为: A 在 1523525484259 离开printA
线程名称为: B 在 1523525484259 进入printB
线程名称为: B 在 1523525484259 离开printB
结论:都是同步的效果,和将sync关键字加到非static 方法上的使用效果是一样的。还是有本质的区别,sync加到static静态方法上是给class类上锁,而sync关键字加到static静态方法上是给对象上锁
例子略,
(2) 验证class类锁,和对象锁无关
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() + "离开rintA");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public static void printB() {
System.out.println("线程名称为: " + Thread.currentThread().getName() + " 在 "
+ System.currentTimeMillis() + "进入printB");
System.out.println("线程名称为: " + Thread.currentThread().getName() + " 在 "
+ System.currentTimeMillis() + "离开printB");
}
}
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printA();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printB();
}
}
public class Run {
public static void main(String[] args) {
Service service1 = new Service();
Service service2 = new Service();
ThreadA a = new ThreadA(service1);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service2);
b.setName("B");
b.start();
}
}
结论:虽说是不同的对象,但sync加在静态方法上是对class上锁,所以还是同步执行。
(3) sync(class)代码和sync static 方法作用一样
public class Service {
public static void printA() {
synchronized (Service.class) {
try {
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ " 在 " + System.currentTimeMillis() + "进入printA");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ " 在 " + System.currentTimeMillis() + "离开printA");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void printB() {
synchronized (Service.class) {
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ " 在 " + System.currentTimeMillis() + "进入printB");
System.out.println("线程名称为:" + Thread.currentThread().getName()
+ " 在 " + System.currentTimeMillis() + "离开printB");
}
}
}
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printA();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.printB();
}
}
public class Run {
public static void main(String[] args) {
Service service1 = new Service();
Service service2 = new Service();
ThreadA a = new ThreadA(service1);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service2);
b.setName("B");
b.start();
}
}