synchronized
是一种同步锁。他修饰的对象有一下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号括起来的代码,作用的对象是调用这个代码块的对象。
- 修饰一个方法,被修饰的方法称为同步语句块,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
- 修饰一个静态的方法,其作用是整个静态方法,作用的对象是这个类的所有对象。
- 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,主作用的对象是这个类的所有对象。
修饰一个代码块
- 一个线程访问一个对象中的同步代码块时:
class SyncThread impltements Runnable{
private static final count;
public SyncThread(){
count = 0;
}
public void run(){
synchronized(this){
for(int i = 0;i<5;i++){
try{
System.out.println(Thread.currentThead().getName()+":"+(count++))
Thread.sleep(100);
}catch(....){
.......
}
}
}
}
public int getCount(){
return count;
}
SyncThread syncThread = new SyncThread();
Thread thread1 = new Thread(syncThread, "11");
Thread thread2 = new Thread(syncThread, "22");
thread1.start();
thread2.start();
}
运行结果:
11:0
11:1
11:2
11:3
11:4
22:5
22:6
22:7
22:8
22:9
当两个并发线程同时访问同一个对象中的同步代码块时,在同一时刻只能有一个线程得到执行,另一个线程受到阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。
但是当我们把运行代码改一下:
Thread thread1 = new Thread(new SyncThread(),"11");
Thread thread2 = new Thread(new SyncThread(), "22");
thread1.start();
thread2.start();
运行结果:
11:0
22:1
11:2
22:3
11:4
22:5
22:6
11:7
11:8
22:9
因为以上代码中synchronized
锁定的是对象,这时会有两把锁分别锁定对象1和对象2,而这两把锁时互不干扰的,不形成互斥,所以两个线程可以同时执行。
2.当一个线程访问对象的一个同步代码块时,另一个线程仍然可以同时访问该对象中的非synchronized
同步代码块。3.指定要给某个对象加锁
class Account{
String name;
int amount;
public Account(String name, int amount){
this.name = name;
this.amount = amount;
}
//存钱
public void deposit(int amt){
amount += amt;
try{
Thread.sleep(100);
}catch(...){
....
}
}
//取钱
public void withdraw(int amt){
amount -= amt;
try{
Thread.sleep(100);
}catch(...){
...
}
}
public int getAmount(){
return amount;
}
}
class AccountOperator implements Runnable{
private Account account;
public void run(){
synchronized(account){
account.deposit(500);
account.withdraw(500);
System.out.println(Thread.currentThread().getName() + ":" + account.getAmount());
}
}
}
//调用代码
Account account = new Account("zhangsan", 10000);
AccountOperator accountOperator = new AccountOperator(account);
final int THREAD_NUM = 5;
Thread[] threads = new Thread[THREAD_NUM];
for(int i = 0;i
在AccountOperator
类的run
方法中,我们用synchronized
给account
对象加了锁。这时,当一个线程访问account
对象时,其他试图访问account
对象的线程将会阻塞,知道该线程访问account
对象结束。
当一个有明确的对象作为�锁时,就可以用类似下面:
public void method3(SomeObject obj){
//obj锁定的对象
synchronized(obj){
//todo
}
}
当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:
class Test implements Runnable{
private byte[] lock = new byte[0];
public void method(){
synchronized(lock){
//todo
}
}
public void run(){
}
}
说明:零长度的byte
数组对象创建起来比任何对象都经济:生成零长度的byte[]
对象只需3条操作码,而Object lock = new Object()
则需要7行操作码。
修饰一个方法
public synchronized void run(){
for(int i = 0;i<5;i++){
try{
System.out.println(Thread.currentThread().getName() + ":" + (count++));
Thread.sleep(100);
}catch(...){
...
}
}
}
synchronized
作用于整个方法的写法:
public synchronized void method(){
//todo
}
或者
public void method(){
synchronized(this){
//todo
}
}
在用synchronized
修饰方法时要注意一下几点:
1.
synchronized
关键字不能继承。
2.在定义接口方法时不能使用synchronized
关键字
3.构造方法不能使用synchronized
关键字,但可以使用sync hronized
代码块来进行同步。
修饰一个静态方法
public synchronized static void method(){
//todo
}
我们知道静态方法属于类而不属于对象的。同样的,synchronized
修饰的静态方法锁定的是这个类的所有对象。
class SyncThread implements Runnable{
private static int count;
public SyncThread(){
count = 0;
}
public synchronized void run(){
}
public synchronized static void method(){
for(int i = 0;i<5;i++){
try{
Log.e(TAG, "method: "+Thread.currentThread().getName()+":"+(count++));
Thread.sleep(100);
}catch(...){
...
}
}
}
}
调用代码:
SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(sychThread1, "SyncThread1");
Thread thread2 = new Thread(sychThread2, "SyncThread2");
thread1.start();
thread2.start();
运行结果:
SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9
syncThread1
和syncThread2
是SyncThread
的两个对象,但在thread1
和thread2
并发执行却保持了线程同步,因为run
中调用了静态方法method
,而静态方法是属于类的,所以syncThread1
和syncThread2
相当于同一把锁。
修饰一个类
class ClassName{
public void method(){
synchronized(ClassName.class){
//todo
}
}
}
class SyncThread implements Runnable{
private static int count;
public SyncThread(){
count = 0;
}
public void run(){
method();
}
public static void method(){
synchronized(SyncThread.class){
for(int i = 0; i<5;i++){
try{
log.e(TAG, "run: "+Thread.currentThread().getName()+":"+count++);
Thread.sleep(100);
}catch(...){
...
}
}
}
}
}
运行结果:
SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9
总结:1.无论synchronized
关键字加在方法还是对象上,如果他作用的对象是非静态的,则它取得的锁是对象,如果synchronized
作用的对象是一个静态方法或一个类,则它取得的锁是类,该类所有的对象同一把锁。
2.每个对象只有一个锁(lock
)与之关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
3.实现同步时要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。