使用synchronized雖然能夠避免不同步的現象出現,但是也會出現弊端,比如代碼執行時間過長,那麼其他線程就必須等待該線程執行完畢釋放鎖之後才能拿到鎖。 面對這種問題可以使用同步代碼塊來解決。 2.2.1synchronized方法的弊端: 任務類: 工具類: 線程代碼1: 線程代碼2: 執行代 ...
使用synchronized雖然能夠避免不同步的現象出現,但是也會出現弊端,比如代碼執行時間過長,那麼其他線程就必須等待該線程執行完畢釋放鎖之後才能拿到鎖。
面對這種問題可以使用同步代碼塊來解決。
2.2.1synchronized方法的弊端:
任務類:
public class Task {
private String getData1;
private String getData2;
synchronized public 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) {
e.printStackTrace();
}
}
}
工具類:
public class CommonUtils {
public static long beginTime1;
public static long endTime1;
public static long beginTime2;
public static long endTime2;
}
線程代碼1:
public class Thread1 extends Thread {
private Task task;
public Thread1(Task task) {
this.task = task;
}
@Override
public void run() {
CommonUtils.beginTime1 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime1 = System.currentTimeMillis();
}
}
線程代碼2:
public class Thread2 extends Thread {
private Task task;
public Thread2(Task task) {
this.task = task;
}
@Override
public void run() {
CommonUtils.beginTime2 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime2 = System.currentTimeMillis();
}
}
執行代碼:
public class Main {
public static void main(String[] args) {
Task task = new Task();
Thread1 thread1 = new Thread1(task);
thread1.start();
Thread2 thread2 = new Thread2(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);
}
}
執行結果:
從結果看這樣運行一段代碼耗時嚴重,解決這樣的問題可以使用synchronized同步代碼塊。
2.2.2synchronized同步代碼塊的使用:
兩個線程同時訪問同一個對象的synchronized(this)同步代碼塊時,在代碼運行期間只能有一個線程執行該段代碼塊,另一個線程必須等待當前線程完成執行才能夠執行該段代碼。
模塊業務類:
public class ObjectService {
public void serviceMethod() {
try {
synchronized (this) {
System.out.println("begin time = " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("end time = " + System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
線程代碼1:
public class Thread3 extends Thread {
private ObjectService objectService;
public Thread3(ObjectService objectService) {
this.objectService = objectService;
}
@Override
public void run() {
objectService.serviceMethod();
}
}
線程代碼2:
public class Thread4 extends Thread {
private ObjectService objectService;
public Thread4(ObjectService objectService) {
this.objectService = objectService;
}
@Override
public void run() {
objectService.serviceMethod();
}
}
執行代碼:
public class Main {
public static void main(String[] args) {
ObjectService objectService = new ObjectService();
Thread3 thread3 = new Thread3(objectService);
thread3.setName("a");
thread3.start();
Thread4 thread4 = new Thread4(objectService);
thread4.setName("b");
thread4.start();
}
}
執行結果:
這樣使用同步代碼塊,並沒有使代碼的效率提高,執行的效果還是同步執行的。下麵的示例中解決synchronized同步代碼塊執行效率低的問題。
2.2.3用同步代碼塊解決同步方法的弊端:
任務類:
public class DoLongTimeTask1 {
private String getData1;
private String getData2;
public void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
String privateData1 = "長時間處理任務後從後臺遠程返回的值1 threadName = "
+ Thread.currentThread().getName();
String privateData2 = "長時間處理任務後從後臺遠程返回的值2 threadName = "
+ Thread.currentThread().getName();
synchronized (this) {
getData1 = privateData1;
getData2 = privateData2;
}
System.out.println(getData1);
System.out.println(getData2);
System.out.println("end task");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
線程代碼1:
public class Thread1 extends Thread {
private Task task;
public Thread1(Task task) {
this.task = task;
}
@Override
public void run() {
CommonUtils.beginTime1 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime1 = System.currentTimeMillis();
}
}
線程代碼2:
public class Thread2 extends Thread {
private Task task;
public Thread2(Task task) {
this.task = task;
}
@Override
public void run() {
CommonUtils.beginTime2 = System.currentTimeMillis();
task.doLongTimeTask();
CommonUtils.endTime2 = System.currentTimeMillis();
}
}
執行代碼:
public class Main {
public static void main(String[] args) {
Task task = new Task();
Thread1 thread1 = new Thread1(task);
thread1.start();
Thread2 thread2 = new Thread2(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);
}
}
執行結果:
從上述可知當一個線程訪問object中的synchronized同步代碼塊時,其他線程可以訪問該object對象中非synchronized(this)同步代碼塊的內容。
時間縮短,且運行效率加快,而且能夠保持synchronized是同步的且當前線程持有鎖。下麵的示例進行驗證。
2.2.4一半非同步,一半同步:
事先說明:不在synchronized塊中的代碼使非同步的,在synchronized中的代碼是同步的。
任務代碼:
public class Task1 {
public void doLongTimeTask() {
for (int i = 0; i < 100; i++) {
System.out.println("nosynchronized threadName = " + Thread.currentThread().getName() + " i = " + (i + 1));
}
System.out.println("");
synchronized (this) {
for (int i = 0; i < 100; i++) {
System.out.println("synchronized threadName = " + Thread.currentThread().getName() + " i = " + (i + 1));
}
}
}
}
線程代碼1:
public class Task1 {
public void doLongTimeTask() {
for (int i = 0; i < 100; i++) {
System.out.println("nosynchronized threadName = " + Thread.currentThread().getName() + " i = " + (i + 1));
}
System.out.println("");
synchronized (this) {
for (int i = 0; i < 100; i++) {
System.out.println("synchronized threadName = " + Thread.currentThread().getName() + " i = " + (i + 1));
}
}
}
}
線程代碼2:
public class Thread6 extends Thread {
private Task1 task;
public Thread6(Task1 task) {
this.task = task;
}
@Override
public void run() {
task.doLongTimeTask();
}
}
執行代碼:
public class Main {
public static void main(String[] args) {
Task1 task = new Task1();
Thread5 thread5 = new Thread5(task);
thread5.start();
Thread6 thread6 = new Thread6(task);
thread6.start();
}
}
執行結果(左邊為非同步,右邊為同步):
可以看出在同步代碼塊中的代碼是同步運行的,而在非同步代碼塊中的代碼是非同步運行的。
2.2.5synchronized代碼塊間的同步性:
若一個線程訪問了object的一個synchronized(this)同步代碼塊時,其他線程對同一個object中所有的其他synchronized(this)同步代碼塊的訪問將被阻塞。
這個現象表明瞭:synchronized使用的是一個對象監視器。