后台的同事在开发随机生成的试题,开发考试这块的功能,从用户登录到生成随机试卷,再到显示到过程中,并发量在到100左右的时候就比较的卡。想想几年前的java线程方面的知识,感觉自己已经忘的差不多了,今天比较空闲,就总结一下java的synchronized关键字,纯属自己的理解,如果有不对的地方还请大家指出,共同进步!
synchronized 是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
一、当两个并发线程访问**同一个对象object中**的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块**(注意的是同一个object)**。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞**(注意的是同一个object)**。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对**该object对象**所有同步代码部分的访问都被暂时阻塞。
举例说明:
package com.tian.threadtestdemo.utils;
/**
*
* @author TCX
*
*/
public class SameObjectTest {
//同步代码块
public void text1(){
//this表示添加的对象锁(this具体指的是什么呢?它指的就是调用这个方法的对象,如P1。)
synchronized (this) {
for(int i=0;i<5;i++){
System.out.println("text1--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//非同步方法体或代码块
public void text2(){
for(int i=0;i<5;i++){
System.out.println("text2--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//同步方法体
public synchronized void text3(){
for(int i=0;i<5;i++){
System.out.println("text3--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void text4(){
**String str=new String();**
synchronized (str) {
for(int i=0;i<5;i++){
System.out.println("text3--->"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试代码如下:
package com.tian.threadtestdemo;
import com.tian.threadtestdemo.utils.SameObjectTest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
public class SameActivity extends Activity implements OnClickListener {
private SameObjectTest text1;
private SameObjectTest text2;
private SameObjectTest text3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_same);
initView();
}
/**
* 初始化页面控件
*/
private void initView() {
View btn1 = findViewById(R.id.btn_1);
View btn2 = findViewById(R.id.btn_2);
View btn3 = findViewById(R.id.btn_3);
View btn4 = findViewById(R.id.btn_4);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_1:
//组合一
startThread1();
break;
case R.id.btn_2:
//组合二
startThread2();
break;
case R.id.btn_3:
//组合三
startThread3();
break;
case R.id.btn_4:
//组合四
startThread4();
break;
default:
break;
}
}
/**
* 启动组合一
*/
private void startThread1() {
text1 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text1.text1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text1.text2();
}
}).start();
}
/**
* 启动组合二
*/
private void startThread2() {
text2 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text2.text1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text2.text3();
}
}).start();
}
/**
* 启动组合三
*/
private void startThread3() {
text3 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text3.text2();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text3.text3();
}
}).start();
}
/**
* 启动组合四
*/
private void startThread4() {
text4 = new SameObjectTest();
new Thread(new Runnable() {
@Override
public void run() {
text4.text1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
text4.text4();
}
}).start();
}
}
组合一的打印结果为:
可以看的出来,打印结果是交替出现,说明了在几乎同时启动子线程去调用同对象中,同步代码块和非同步代码块的同时,同步代码块并没有阻塞非同步代码块的运行
组合二的打印结果为:
可以看的出来,打印结果不是交替出现,说明了在几乎同时启动子线程去调用同对象中,添加相同的锁的代码块,在执行过程中发生了阻塞,在text1()方法体执行完之后,才对text3()执行。
组合三的打印结果为:
结果跟组合一的打印结果一致。不多解释了。。。
组合四的打印结果为:
注意的是text4()方法体中的锁对象变化了哦!
String str=new String();
synchronized (str){
//、、、
}
对象锁发生了变化,当然不同步咯!哈哈哈!
synchronized的拓展
1.假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加synchronized关键字的方法。
2.如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class,当然也不会产生同步的效果。
当然,使用同步锁可以提高我们代码的准确性,但是并不是所有的情况都适用。适用同步锁对程序的开销是非常大的,搞不好还会出现死锁的情况,所以呢,还是慎用哦!