对于很多刚刚接触多线程编程的同学来说,可能仅仅是听说过线程同步和线程安全这两个名词而已,对于具体什么线程同步、什么是线程安全,可能也只能从他的名字上面去猜测一下他的意思,至于其他的可能并没有太多的了解,因此我决定写一篇这样的文章,详细地告诉大家,线程同步和线程安全究竟是何方神圣!
首先究竟什么是线程同步呢?在了解线程同步之前,我们先了解一下什么是同步,这有助于对线程同步的理解。所谓同步,就是在发出一个方法的调用时,在没有得到结果之前,这个调用就不返回,同时其它的线程也不能调用这个方法!线程同步也是类似的意思,但线程同步不是说让一个线程执行完了再执行其它线程,一般是指让线程中的某一些操作进行同步就可以了、下面我举个例子:让大家更好地进行理解。
而在多线程的编程里,我们不可避免地会遇上到这样的一种问题,就是一些数据不能被多个线程同时访问,比如A和B同时去商店里买糖,但商店里一共就只剩下3个糖了,A说我要2个,B同时也说我要2个,那么此时是不是A和B中肯定有一个人买不到2个糖!如果生活中还好,还可以商量着解决!那么线程里有没有类似商量解决的方法呢?其实多线程里面也是有类似的方法的,这就先让A或B中的一个买,假设是A先买,等A先买完了,B再买!刚好同步机制就可以解决上面这个问题,解决让谁先买,谁后买的问题. 采用同步机制就可以保证数据在任何时候最多只有一个线程进行访问、从而保证了数据的安全!
等A和B处理完买糖的事之后,他们是不是想干嘛就干嘛、所以说线程同步,一般是指让线程中的某一些操作进行同步就可以了、
Java是如何做到线程同步的呢?
- 在需要同步的方法的 方法签名中加上 synchronized关键字
- 使用synchronized关键字对需要进行同步的代码块进行同步
- 使用java.util.concurrent.lock包中Lock对象(JDK1.8)
1.在需要同步的方法的 方法签名中加上 synchronized关键字
public class Data {
private int mVal = 0;
public void setDate(int mVal) {
this.mVal = mVal;
}
public int getDate() {
return mVal;
}
}
public class MyThread implements Runnable{
private Data mData = new Data();
@Override//让多个线程共享一个数据
public synchronized void run() {
int tmp = mData.getDate();
++tmp;
mData.setDate(tmp);
System.out.println(Thread.currentThread().getName()+"|"+mData.getDate());
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(myThread);
thread.start();
}
}
}
//打印的结果
Thread-0|1
Thread-4|2
Thread-3|3
Thread-2|4
Thread-1|5
public class MyThread implements Runnable{
private Data mData = new Data();
@Override//注释了synchronized关键字后再看结果
public /*synchronized*/ void run() {
int tmp = mData.getDate();
++tmp;
mData.setDate(tmp);
System.out.println(Thread.currentThread().getName()+"|"+mData.getDate());
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(myThread);
thread.start();
}
}
}
//注释了synchronized关键字后
Thread-0|2
Thread-4|5
Thread-3|4
Thread-2|3
Thread-1|2
//需要注意的是,运行的结果肯定每次都是不同的,原因:线程抢占到资源的顺序
2. 使用synchronized关键字对需要进行同步的代码块进行同步 (Data类还是一样)
public class MyThread implements Runnable{
private Data mData = new Data();
@Override//使用synchronized代码块(同步块)
public void run() {
synchronized (this) {
int tmp = mData.getDate();
++tmp;
mData.setDate(tmp);
System.out.println(Thread.currentThread().getName()+"|"+mData.getDate());
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(myThread);
thread.start();
}
}
}
//打印的结果
Thread-0|1
Thread-4|2
Thread-3|3
Thread-2|4
Thread-1|5
3. 使用java.util.concurrent.lock包中Lock对象(JDK1.8) 【有需要的时候再补充】
synchronized使用时需要注意的一些地方:
被synchronized关键字修饰的代码块在被线程执行之前,首先要拿到被同步对象的锁,并且一个对象仅仅是只有一个锁,比如上面被synchronized代码,首先那个方法需要拿到当前对象的锁,如果当前的锁已经被其它线程拿走了,那么还没抢到锁的线程将从可运行状态转变为阻塞状态,只有当拿到锁的线程执行完同步块的代码后,就释放锁,让给别的线程的、这样就可以保证数据的完整性!