- 简介
synchronized是Java中的的关键字,是一种同步锁,可以修饰以下几种
1.1>:类
作用的范围是:synchronized后边括号括起来的部分
作用的对象是:这个类当中所有的对象
1.2>:静态方法
作用的范围是:整个静态方法
作用的对象是:这个类当中的所有对象
1.3>:代码块:被修饰的代码块称为同步代码块
作用的范围是:大括号括起来的部分
作用的对象是:调用这个代码块的对象
1.4>:方法:被修饰的方法称为同步方法
作用的范围是:整个方法
作用的对象是:调用这个方法的对象 - 实例分析
2.1>: synchronized修饰的代码块
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity" ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SyncThread syncThread = new SyncThread() ;
Thread thread1 = new Thread(syncThread , "SyncThread1") ;
Thread thread2 = new Thread(syncThread , "SyncThread2") ;
thread1.start();
thread2.start();
}
class SyncThread implements Runnable{
private int count ;
public SyncThread(){
count = 0 ;
}
@Override
public void run() {
synchronized (this){
for (int i = 0; i < 5; i++) {
try {
Log.d("mainActivity-->", Thread.currentThread().getName() + ":" + (count++)) ;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public int getCount(){
return count ;
}
}
}
上边运行结果是
//SyncThread2:0
//SyncThread2:1
//SyncThread2:2
//SyncThread2:3
//SyncThread2:4
//SyncThread1:5
//SyncThread1:6
//SyncThread1:7
//SyncThread1:8
//SyncThread1:9
由以上可知:
当两个线程thread1和thread2访问 "同一个syncThread对象" 中的synchronized代码块,同一时刻只能一个执行线程,另一个受阻,必须等当前线程执行完这个代码块后才能执行,thread1在执行synchronized代码块时会锁定当前对象,只有执行完该代码块后才能释放对象锁,下一个线程才能执行并锁定该对象
我们现在把
Thread thread1 = new Thread(syncThread , "SyncThread1") ;
Thread thread2 = new Thread(syncThread , "SyncThread2") ;
这两句修改为
Thread thread1 = new Thread(new SyncThread() , "SyncThread1") ;
Thread thread2 = new Thread(new SyncThread() , "SyncThread2") ;
运行结果是
//SyncThread1:0
//SyncThread2:0
//SyncThread1:1
//SyncThread2:1
//SyncThread1:2
//SyncThread2:2
//SyncThread1:3
//SyncThread2:3
//SyncThread1:4
//SyncThread2:4
现象:
thread1和thread2同时在执行
原因:
synchronized修饰代码块时,只锁定代码块中的对象,一个对象只有一个锁 [ lock ] 与之关联,而上边代码等同于
SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();
这个时候创建了2个对象,分别为 syncThread1何syncThread2,线程thread1执行syncThread1对象中对应synchronized的代码,线程thread2执行syncThread2中对应的synchronized对象,我们都知道synchronized锁定的是对象,这个时候会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁互补干扰,所以两个线程可以同时执行
代码:https://github.com/shuai999/ThreadDemo
2.2:当一个线程访问一个对象的synchronized(this)代码块时,其他线程也可以访问该对象的非synchronized(this)代码块,并且不受阻塞
多个线程访问同一个对象的synchronized(this)代码块和非synchronized(this)代码块,示例如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Counter counter = new Counter() ;
//参数一:Runnable的实现类 参数二:线程名
Thread thread1 = new Thread(counter , "A") ;
Thread thread2 = new Thread(counter , "B") ;
thread1.start();
thread2.start();
}
class Counter implements Runnable{
private int count ;
public Counter(){
count = 0 ;
}
public void countAdd(){
//synchronized代码块
synchronized (this){
for (int i = 0; i < 5; i++) {
try {
Log.d("threadName--->" , Thread.currentThread().getName() +":" + (count++)) ;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//非synchronized代码块,未对count进行读写操作,所以可以不用synchronized
public void printAdd(){
for (int i = 0; i < 5; i++) {
try {
Log.d("threadName--->" , Thread.currentThread().getName() + "count:" + count) ;
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
String threadName = Thread.currentThread().getName() ;
if (threadName.equals("A")){
countAdd();
}else if (threadName.equals("B")){
printAdd();
}
}
}
// Bcount:0
// A:0
// Bcount:1
// A:1
// Bcount:2
// A:2
// Bcount:3
// A:3
// Bcount:4
// A:4
/* 以上是运行结果 */
/* 由结果可知,countAdd()方法是synchronized代码块,printAdd()不是synchronized代码块。
当一个线程访问一个对象的synchronized代码块时,其他的线程可以访问该对象的非synchronized代码块而不受阻塞*/
}
具体代码已上传至github:
https://github.com/shuai999/ThreadDemo3
2.3:指定给某个对象加锁
代码如下:
/**
* @author : Created by ces
* @date: on 2018/1/28.
* @function: 指定给某个对象加锁
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Account account = new Account("zhangsan" , 10000.0f) ;
AccountOperator operator = new AccountOperator(account) ;
final int THREAD_NUM = 5 ;
Thread threads[] = new Thread[THREAD_NUM] ;
for (int i = 0; i < THREAD_NUM; i++) {
threads[i] = new Thread(operator , "Thread" + i);
threads[i].start();
}
}
public class AccountOperator implements Runnable{
private Account account ;
public AccountOperator(Account account){
this.account = account ;
}
@Override
public void run() {
synchronized (account){
//存钱500
account.deposit(500);
//取钱500
account.withDraw(500);
Log.e("cesAccount--->", Thread.currentThread().getName() + ":" + account.getBalance()) ;
//运行结果如下
// Thread0:10000.0
// Thread1:10000.0
// Thread2:10000.0
// Thread3:10000.0
// Thread4:10000.0
}
}
}
}
/**
* @author : Created by ces
* @date: on 2018/1/28.
* @function: 指定给某个对象加锁
*/
public class Account {
String name ;
float amount ;
public Account(String name , float amount){
this.name = name ;
this.amount = amount ;
}
//存钱
public void deposit(float amt){
amount +=amt;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//取钱
public void withDraw(float amt){
amount -=amt ;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public float getBalance(){
return amount ;
}
}
在AccountOperator类中,我们给account对象加了锁,这时当一个线程访问account对象时,其他线程就会阻塞,直到该线程访问account对象结束,也就是说谁拿到那个锁,谁就可以运行它所控制的那段代码。
具体代码已上传至github
https://github.com/shuai999/ThreadDemo4.git
2.4 修饰一个方法
格式:public synchronized void method(){//todo};
synchronized修饰方法和代码块类似,只是作用范围不一样:
修饰的代码块是大括号括起来的范围;
修饰的方法是整个函数;
在用synchronized修饰方法时注意以下几点:
1.synchronized关键字不能被继承
1.1 :如果在父类中某个方法使用了synchronized,而在子类中覆盖此方法,在子类中这个方法默认并不是同步的,必须显式的在方法中加synchronized才可以;
1.2:还可以在子类方法中调用父类中相应的方法,这样虽然子类中方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了,这两种方式代码如下:
在子类方法上边加上synchronized:
class Parent{
public synchronized void method(){}
}
class Child extends Parent{
public synchronized void method(){}
}
在子类方法中调用父类的同步方法:
class Parent{
public synchronized void method(){}
}
class Childe extends Parent{
public void method(){
super.method() ;
}
}
注意:
1>:在定义接口方法时不能使用synchronized
2>:在构造方法中不能用synchronized,但可以使用synchronized代码块来进行同步
2.5:修饰一个静态方法,,代码如下
public synchronized static void method() {
// todo
}
2.6:修饰一个类,代码如下:
class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
总结:
1>:无论synchronized加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的是一个静态方法或一个类,则它取得的对象是该类,该类所有的对象是同一把锁;
2>:每个对象只有一个锁 [ lock ]与之相关联,谁拿到这个锁,谁就可以运行它所控制的那段代码;
3>:实现同步是需要很大的系统开销作为代价,甚至造成死锁,所以尽量避免无谓的同步控制。