引:
由于同一进程内的多个线程共享内存空间,在Java中,就是共享实例,当多个线程试图同时修改某个实例的内容时,就会造成冲突,因此,线程必须实现共享互斥,使多线程同步。
最简单的同步是将一个方法标记为synchronized,对同一个实例来说,任一时刻只能有一个synchronized方法在执行。当一个方法正在执 行某个synchronized方法时,其他线程如果想要执行这个实例的任意一个synchronized方法,都必须等待当前执行 synchronized方法的线程退出此方法后,才能依次执行。
但是,非synchronized方法不受影响,不管当前有没有执行synchronized方法,非synchronized方法都可以被多个线程同时执行。
资源竞争问题:
1、线程的特点:”你永远不知道一个线程在何时运行,想象一下,当你坐在桌子旁边,正要去叉最后一块实物的时候,食物消失了,因为你线程被挂起,别的人吃掉了这块食物“
2、同步规则:
如果你正在写一个变量,他可能被另一个线程读取,或者你正在读取上一次已经被别的线程写过的变量,那么你必须使用同步;
3、锁语句:
锁语句产生了一种互相排斥的效果,所以这种机制也叫互斥量(mutex)
JAVA提供了关键字synchronized的形式,为防止资源冲突提供了内置支持,当任务要执行被保护的代码片段时候,他将会检查锁是否可用,然后获取锁,执行代码,然后是释放锁;
4、共享资源一般是以对象的形式存在的内存片段,但也可以是文件、输入输出端口,打印机;
5、对象都自动含有单一的锁(也叫监视器),针对每个类也有一个锁,所以synchronized static方法 可以在类的范围防止对static数据的并发访问
6、如果你的类中有多个方法在处理临界区的数据,每一个访问临界区共享资源的方法必须被同步,否则不会正常工作
同步格式写法
synchonized关键字
方法同步:用关键字 synchonized 可将方法声明为同步;格式如下。
class 类名{
public synchonized 类型名称 方法名称(){
......
}
}
参考这个例子:http://lavasoft.blog.51cto.com/62575/221914
语句块同步: 对于同步块,在进入代码片之前,必须获得obj对象的锁。
synchornized(obj)
{
//………………….
}
和同步方法有小小的不同。
1.从尺寸上讲,同步代码块比同步方法小。你可以把同步代码块看成是没上锁房间里的一块用带锁的屏风隔开的空间。
你也可以指定用另一个房子的钥匙才能开,这样的话,你要跑到另一栋房子那儿把那个钥匙拿来,并用那个房子的钥匙来打开这个房子的带锁的屏风。
记住你获得的那另一栋房子的钥匙,并不影响其他人进入那栋房子没有锁的房间。
eg:
返回ImageLoader的单例模式
private volatile static ImageLoader instance;
/** Returns singleton class instance */
public static ImageLoader getInstance() {
if (instance == null) {
synchronized (ImageLoader.class) {
if (instance == null) {
instance = new ImageLoader();
}
}
}
return instance;
}
这样就可以在类的范围防止对static数据的并发访问;
同步方法也可以改写为非同步方法,但功能完全一样的,例如:
public synchronized int getX() {
return x++;
}
与
public int getX() {
synchronized (this) {
return x;
}
}
效果是完全一样的。
静态方法同步:要同步静态方法,需要一个用于整个类对象的锁,这个对象是就是这个类(XXX.class)。
例如:
public static synchronized int setName(String name){
Xxx.name = name;
}
等价于
public static int setName(String name){
synchronized(Xxx.class){
Xxx.name = name;
}
}
至于显式的Lock对象就不涉及了,某些特殊情况才会考虑;
原子性,volatile关键字
原子性就是说一个操作不可以被中途cpu暂停然后调度, 即不能被中断, 要不就执行完, 要不就不执行. 如果一个操作是原子性的, 那么在多线程环境下, 就不会出现变量被修改等奇怪的问题
volatile关键字
volatile关键字是用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致的情况。volatile就是用来避免这种情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)
除了synchronized是对方法的同步这个外,Volatile一般情况下也不能代替sychronized,因为volatile很多情况下不能保证操作的原子性,即使只是i++,实际上也是由多个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。
如n=n+1、n++ 等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。
public class SynchronizedTest implements Runnable{
private volatile int x=100;
public static void main(String[] args) {
SynchronizedTest r=new SynchronizedTest();
Thread t1=new Thread(r,"thread1");
Thread t2=new Thread(r,"thread2");
t1.start();t2.start();
}
@Override
public void run() {
for(int i=0;i<5;i++){
int m;
m=x+1;
x=m;
System.out.println(Thread.currentThread().getName()+"i的值"+m);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
虽然2个线程执行的时间不固定,但是x的值确实同步了(没有重复的)
thread2i的值101
thread1i的值102
thread2i的值103
thread1i的值104
thread1i的值106
thread1i的值107
thread1i的值108
thread2i的值105
thread2i的值109
thread2i的值110
在上面的run方法里面加上synchronized(this),
synchronized (this) {
for (int i = 0; i < 5; i++) {
int m;
m = x + 1;
x = m;
System.out
.println(Thread.currentThread().getName() + "i的值" + m);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
得到的结果就是
thread1i的值101
thread1i的值102
thread1i的值103
thread1i的值104
thread1i的值105
thread2i的值106
thread2i的值107
thread2i的值108
thread2i的值109
thread2i的值110
附:
为了提高性能,有时不一定要锁定this,例如,SharedResource有两个独立变化的变量:
public class SharedResouce {
private int a = 0;
private int b = 0;
public synchronized void setA(int a) { this.a = a; }
public synchronized void setB(int b) { this.b = b; }
}
若同步整个方法,则setA()的时候无法setB(),setB()时无法setA()。为了提高性能,可以使用不同对象的锁:
public class SharedResouce {
private int a = 0;
private int b = 0;
private Object sync_a = new Object();
private Object sync_b = new Object();
public void setA(int a) {
synchronized(sync_a) {
this.a = a;
}
}
public synchronized void setB(int b) {
synchronized(sync_b) {
this.b = b;
}
}
}
参考:http://www.blogjava.net/parable-myth/archive/2008/08/06/220424.html