java多线程(三)——锁机制synchronized(同步语句块)

时间:2022-11-16 15:55:52

用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解决。

一、用同步代码块解决同步方法的弊端

Task类

 package com.weishiyao.learn.day4.testSynchorized.ep2;

 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) {
e.printStackTrace();
}
}
}

常量工具类

 package com.weishiyao.learn.day4.testSynchorized.ep2;

 public class CommonUtils {

     public static long beginTime1;
public static long endTime1; public static long beginTime2;
public static long endTime2;
}

线程类——2个

 package com.weishiyao.learn.day4.testSynchorized.ep2;

 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();
} }
 package com.weishiyao.learn.day4.testSynchorized.ep2;

 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();
} }

运行类

 package com.weishiyao.learn.day4.testSynchorized.ep2;

 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-1
长时间处理任务后从远程返回的值1 threadName=Thread-0
长时间处理任务后从远程返回的值2 threadName=Thread-0
长时间处理任务后从远程返回的值2 threadName=Thread-0
end task
end task
耗时3 秒

这里是用的synchronized代码锁,如果换成方法锁

所有代码不变,仅更改Task类

 package com.weishiyao.learn.day4.testSynchorized.ep2;

 public class Task {

     private String getData1;
private String getData2; public synchronized void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
getData1 = "长时间处理任务后从远程返回的值1 threadName="
+ Thread.currentThread().getName();
getData2 = "长时间处理任务后从远程返回的值2 threadName="
+ Thread.currentThread().getName();
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

运行结果

 begin task
长时间处理任务后从远程返回的值1 threadName=Thread-0
长时间处理任务后从远程返回的值2 threadName=Thread-0
end task
begin task
长时间处理任务后从远程返回的值1 threadName=Thread-1
长时间处理任务后从远程返回的值2 threadName=Thread-1
end task
耗时6 秒

可以得出结论,当一个线程访问object的synchronized同步代码块时,另一个线程依然可以访问非同步代码块,这样同步代码块就会比同步方法所花费更短的时间,可以得到更高的效率,在同步代码块中代码是同步的,不在同步代码块中代码是异步的。

二、synchronized代码块间的同步性

当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,因为synchronized使用的是一个“对象监视器”

ObjectService类

 package com.weishiyao.learn.day4.testSynchorized.ep3;

 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());
}
}
}

ThreadA

 package com.weishiyao.learn.day4.testSynchorized.ep3;

 public class ThreadA extends Thread {

     private ObjectService service;

     public ThreadA(ObjectService service) {
super();
this.service = service;
} @Override
public void run() {
super.run();
service.serviceMethodA();
} }

ThreadB

 package com.weishiyao.learn.day4.testSynchorized.ep3;

 public class ThreadB extends Thread {
private ObjectService service; public ThreadB(ObjectService service) {
super();
this.service = service;
} @Override
public void run() {
super.run();
service.serviceMethodB();
}
}

Run

 package com.weishiyao.learn.day4.testSynchorized.ep3;

 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=1459077186249
A end end=1459077188249
B begin time=1459077188249
B end end=1459077188249

三、静态同步synchronized方法与synchronized(class)代码块

关键字synchronized还可以用在static静态方法上,如果这样写,那是对当前的java对应的Class类进行上锁

Service类

 package com.weishiyao.learn.day4.staticSynchorized;

 public class Service {
synchronized public static void printA() {
try {
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入A");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开A");
} catch (Exception e) {
e.printStackTrace();
}
} synchronized public static void printB() {
try {
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入B");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开B");
} catch (Exception e) {
e.printStackTrace();
}
} synchronized public void printC() {
try {
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "进入C");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName() + "在" + System.currentTimeMillis() + "离开C");
} catch (Exception e) {
e.printStackTrace();
}
}
}

ThreadA

 package com.weishiyao.learn.day4.staticSynchorized;

 public class ThreadA extends Thread {
private Service service; public ThreadA(Service service) {
super();
this.service = service;
} @SuppressWarnings("static-access")
@Override
public void run() {
service.printA();
}
}

ThreadB、ThreadC类似ThreadA,不再列出

Run

 package com.weishiyao.learn.day4.staticSynchorized;

 public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("A");
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.setName("B");
threadB.start();
ThreadC threadC = new ThreadC(service);
threadC.setName("C");
threadC.start();
}
}

结果

 线程名称为:A在1459078101483进入A
线程名称为:C在1459078101490进入C
线程名称为:A在1459078104484离开A
线程名称为:B在1459078104484进入B
线程名称为:C在1459078104491离开C
线程名称为:B在1459078107484离开B

分析运行结果,A和B是同步运行,C是异步运行,异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁。

同步synchronized(class)代码块的作用和synchronized static方法的作用一样。

四、内置类与同步

OutClass

 package com.weishiyao.learn.day4.syncClass.ep5;

 public class OutClass {
static class InnerClass1 {
public void method1(InnerClass2 class2) {
String threadName = Thread.currentThread().getName();
synchronized (class2) {
System.out.println(threadName
+ " 进入InnerClass1类中的method1方法");
for (int i = 0; i < 10; i++) {
System.out.println("i=" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
System.out.println(threadName
+ " 离开InnerClass1类中的method1方法");
}
} public synchronized void method2() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " 进入InnerClass1类中的method2方法");
for (int j = 0; j < 10; j++) {
System.out.println("j=" + j);
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
System.out.println(threadName + " 离开InnerClass1类中的method2方法");
}
} static class InnerClass2 {
public synchronized void method1() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " 进入InnerClass2类中的method1方法");
for (int k = 0; k < 10; k++) {
System.out.println("k=" + k);
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
}
System.out.println(threadName + " 离开InnerClass2类中的method1方法");
}
}
}

Run

 package com.weishiyao.learn.day4.syncClass.ep5;

 import com.weishiyao.learn.day4.syncClass.ep5.OutClass.InnerClass1;
import com.weishiyao.learn.day4.syncClass.ep5.OutClass.InnerClass2; public class Run { public static void main(String[] args) {
final InnerClass1 in1 = new InnerClass1();
final InnerClass2 in2 = new InnerClass2();
Thread t1 = new Thread(new Runnable() {
public void run() {
in1.method1(in2);
}
}, "T1");
Thread t2 = new Thread(new Runnable() {
public void run() {
in1.method2();
}
}, "T2");
Thread t3 = new Thread(new Runnable() {
public void run() {
in2.method1();
}
}, "T3");
t1.start();
t2.start();
t3.start();
}
}

结果

 T2 进入InnerClass1类中的method2方法
T1 进入InnerClass1类中的method1方法
i=0
j=0
i=1
j=1
j=2
i=2
i=3
j=3
j=4
i=4
i=5
j=5
j=6
i=6
i=7
j=7
i=8
j=8
i=9
j=9
T2 离开InnerClass1类中的method2方法
T1 离开InnerClass1类中的method1方法
T3 进入InnerClass2类中的method1方法
k=0
k=1
k=2
k=3
k=4
k=5
k=6
k=7
k=8
k=9
T3 离开InnerClass2类中的method1方法

同步代码块synchronized(class2)对class2上锁后,其他线程只能以同步的方式调用class2中的静态同步方法

五、锁对象的改变

在将任何数据类型作为同步锁时,需要注意的是,是否有多个线程同时持有锁对象,如果同时持有相同的锁对象,则这些线程之间就是同步的;如果分别获得锁对象,这些线程之间就是异步的。

MyService

 package com.weishiyao.learn.day4.syncClass.ep6;

 public class MyService {
private String lock = "123"; public void testMethod() {
try {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " begin "
+ System.currentTimeMillis());
lock = "456";
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " end "
+ System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} }

ThreadA

 package com.weishiyao.learn.day4.syncClass.ep6;

 public class ThreadA extends Thread {

     private MyService service;

     public ThreadA(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.testMethod();
}
}

ThreadB

package com.weishiyao.learn.day4.syncClass.ep6;

public class ThreadB extends Thread {

    private MyService service;

    public ThreadB(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.testMethod();
}
}

Run1

 package com.weishiyao.learn.day4.syncClass.ep6;

 public class Run1 {

     public static void main(String[] args) throws InterruptedException {

         MyService service = new MyService();

         ThreadA a = new ThreadA(service);
a.setName("A"); ThreadB b = new ThreadB(service);
b.setName("B"); a.start();
Thread.sleep(50);
b.start();
}
}

结果

 A begin 1459080143796
B begin 1459080143846
A end 1459080145797
B end 1459080145846

因为50毫秒之后a取得的是锁"456"

重新改一下Run类

 package com.weishiyao.learn.day4.syncClass.ep6;

 public class Run2 {

     public static void main(String[] args) throws InterruptedException {

         MyService service = new MyService();

         ThreadA a = new ThreadA(service);
a.setName("A"); ThreadB b = new ThreadB(service);
b.setName("B"); a.start();
b.start();
}
}

结果

 A begin 1459080318782
A end 1459080320782
B begin 1459080320782
B end 1459080322783

只要对象不变,即使对象的属性被改变,运行结果还是同步的

Service

 package com.weishiyao.learn.day4.syncClass.ep7;

 public class Service {

     public void serviceMethodA(Userinfo userinfo) {
synchronized (userinfo) {
try {
System.out.println(Thread.currentThread().getName());
userinfo.setUsername("abcabcabc");
Thread.sleep(3000);
System.out.println("end! time=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Userinfo

 package com.weishiyao.learn.day4.syncClass.ep7;

 public class Userinfo {
private String username;
private String password; public Userinfo() {
super();
} public Userinfo(String username, String password) {
super();
this.username = username;
this.password = password;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} }

ThreadA

 package com.weishiyao.learn.day4.syncClass.ep7;

 public class ThreadA extends Thread {

     private Service service;
private Userinfo userinfo; public ThreadA(Service service,
Userinfo userinfo) {
super();
this.service = service;
this.userinfo = userinfo;
} @Override
public void run() {
service.serviceMethodA(userinfo);
} }

ThreadB

 package com.weishiyao.learn.day4.syncClass.ep7;

 public class ThreadB extends Thread {

     private Service service;
private Userinfo userinfo; public ThreadB(Service service,
Userinfo userinfo) {
super();
this.service = service;
this.userinfo = userinfo;
} @Override
public void run() {
service.serviceMethodA(userinfo);
} }

运行类

 package com.weishiyao.learn.day4.syncClass.ep7;

 public class Run {

     public static void main(String[] args) {

         try {
Service service = new Service();
Userinfo userinfo = new Userinfo(); ThreadA a = new ThreadA(service, userinfo);
a.setName("a");
a.start();
Thread.sleep(50);
ThreadB b = new ThreadB(service, userinfo);
b.setName("b");
b.start(); } catch (InterruptedException e) {
e.printStackTrace();
} }
}

结果

 a
end! time=1459080585999
b
end! time=1459080589000