一、用关键字synchronized声明方式时,在某些情况下是有弊端的。比如线程A调用同步方法执行一个长时间的任务,那么线程B必须等待很长的时间即A 线程执行完了才可以开始执行,这样是非常耗时的,在这种情况下就可以使用synchronized同步语句块来实现了。
二、synchronized同步代码块的使用
1.synchronized(this)
(1)当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只有一个线程被执行,另一个线程必须等待当前线程完成这个代码块以后才能执行该代码块,这和synchronized同步方法是一样的。
(2)用同步代码块解决同步方法弊端,即当一个线程访问object的一个synchronized同步代码块时,另一个线程仍然可以访问该object对象中的非synchromized(this)同步代码块。在synchronized块中就是同步执行,不在synchronized块中就是异步执行。
Task.java
public class Task {
public void doLongTimeTask(){
for(int j=0;j<10;j++){
System.out.println("nosyschronized threadName="+Thread.currentThread().getName()+" j= "+j);
}
System.out.println("***********************");
synchronized (this) {
for(int i=0;i<10;i++){
System.out.println("synchronized threadName="+Thread.currentThread().getName()+" i="+i);
}
}
}
}
Thread1.java
public class Thread1 extends Thread{
private Task task;
public Thread1(Task task){
super();
this.task=task;
}
public void run(){
super.run();
task.doLongTimeTask();
}
}
Thread2.java
public class Thread2 extends Thread{
private Task task;
public Thread2(Task task) {
super();
this.task=task;
}
public void run(){
super.run();
task.doLongTimeTask();
}
}
Run.java
public class Run {
public static void main(String[] args){
Task task=new Task();
Thread1 a=new Thread1(task);
a.setName("A");
a.start();
Thread2 b=new Thread2(task);
b.setName("B");
b.start();
}
}
(3)synchronized代码块之间的同步性
当一个线程访问object的synchronized(this)同步代码块时,其他线程对同一个object中所有其他的synchronized(this)同步代码块的访问将被阻塞。
ObjectService.java
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 time="+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 time="+System.currentTimeMillis());
}
}
}
(4)synchronized(this)代码块时锁定当前对象的
Task.java
public class Task {
public void otherMethod(){
System.out.println("**********");
}
public void doLongTimeTask(){
synchronized (this){
for(int i=0;i<10000;i++){
System.out.println("synchronized threadname="+Thread.currentThread().getName()+" i="+i);
}
}
}
}
一个synchronized(this)同步代码块,一个非synchronized(this)同步代码块,A线程调和B线程调用同一个对象,A线程调用doLongTimeTask方法,B线程调用otherMethod。执行结果是异步打印的。结果如下:
更改Task.java,即两个代码块都是synchronized(this)同步代码块,代码如下:
public class Task {
synchronized public void otherMethod(){
System.out.println("**********");
}
public void doLongTimeTask(){
synchronized (this){
for(int i=0;i<10000;i++){
System.out.println("synchronized threadname="+Thread.currentThread().getName()+" i="+i);
}
}
}
}
结果如下,执行时是同步的 :
2.把任意对象作为对象监视器
多个线程调用同一个对象中的不同名称的synchronized同步方法或者synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
java不光支持synchronized(this)实现同步代码块,还支持任意对对象作为对象监视器来实现同步代码块,这个任意对象大多是指实例变量以及方法的参数。
使用格式为synchronized(非this对象x)。
Service.java
public class Service {
String anything=new String();
public void testMethod1(){
synchronized (anything) {
try{
System.out.println("testMethod1_getlock time="+System.currentTimeMillis()+" run ThreadName="
+Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("testNethod1 releaselock time="+System.currentTimeMillis()+" run ThreadName="+Thread.currentThread().getName());
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
Run1_1.java
public class Run1_1 {
public static void main(String[] args){
Service service=new Service();
ThreadA a=new ThreadA(service);
a.setName("A");
a.start();
ThreadB b=new ThreadB(service);
b.setName("B");
b.start();
}
}
Service里的anything属于全局变量,所以线程A、B执行的是同一对象,因此结果是同步的,结果如下:
锁非this对象具有一定的优点,如果不是同一个对象监视器,运行的结果是异步调用。
把Service.java改成如下:
public class Service {
//String anything=new String();
public void testMethod1(){
String anything=new String();
synchronized (anything) {
try{
System.out.println("testMethod1_getlock time="+System.currentTimeMillis()+" run ThreadName="
+Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("testNethod1 releaselock time="+System.currentTimeMillis()+" run ThreadName="+Thread.currentThread().getName());
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
把anything放在testMethod1里面,每次调用这个方法时都会new一个新的anything,因此时异步执行,结果如下:
还有一个例子,service1.java
public class Service1 {
private String anyString=new String();
public void a(){
try{
synchronized (anyString) {
System.out.println("a begin");
Thread.sleep(3000);
System.out.println("a end");
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
synchronized public void b(){
System.out.println("b begin");
System.out.println("b end");
}
}
MyThread1.java
public class MyThread1 extends Thread{
private Service1 service;
public MyThread1(Service1 service) {
super();
this.service=service;
}
public void run(){
service.a();
}
}
MyThread2.java
public class MyThread2 extends Thread{
private Service1 service;
public MyThread2(Service1 service) {
super();
this.service=service;
}
public void run(){
service.b();
}
}
Run1.java
public class Run1 {
public static void main(String[] args){
Service1 service=new Service1();
MyThread1 a=new MyThread1(service);
a.setName("A");
a.start();
MyThread2 b=new MyThread2(service);
b.setName("B");
b.start();
}
}
结果如下:
3.同步代码块在非同步synchronized方法中进行声明,并不能保证调用方法的线程的执行同步、顺序性,即线程的调用方法的顺序是无序的,在同步块中执行顺序是同步的。
4.对synchronized(非this对象x)总结
synchronized(非this对象x)把x对象本身作为对象监视器。
(1)当多个线程同时执行synchronized(x)同步代码块时呈现同步效果。
(2)当其他线程执行x对象中synchronized同步方法时呈现同步效果。
(3)当其他线程执行x对象方法里的synchronized(this)代码块时爷呈现同步效果。
5.静态同步synchronized方法
synchronized应用在static静态方法上,即对当前的.java文件对应的class类进行持锁。
synchronized加到static方法上是给class类上锁,而synchronized加到非静态方法上是给对象上锁,代码如下:
Service.java
/*
* 同一个锁的执行是同步的
* 不同锁的执行时异步的(对象锁和class锁)
* 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()+"离开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");
}
synchronized public void printC(){
System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"进入printC");
System.out.println("线程名称为:"+Thread.currentThread().getName()+"在"+System.currentTimeMillis()+"离开printC");
}
}
结果如下:
class锁可以对类的所有对象实例起作用:
Service.java
/*
* class锁对于所有的对象实例都起作用
* 线程操作不同的实例对象,对象锁执行时是异步执行的
* 但是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()+"离开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");
}
}
Run.java
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();
}
}
j结果如下:
若是对象锁,结果就是异步的了。