线程同步
按照百度的简介:
在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。
“同”字从字面上容易理解为一起动作
其实不是,“同”字应是指协同、协助、互相配合。
如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。例如Window API函数SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者。
在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
作用
线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。
线程同步的方法:
(1)wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
(2)sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉 InterruptedException异常。
(3)notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的 唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
(4)notityAll ():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁, 而是让它们竞争。
wait()方法
简单地说,当我们调用wait()时 -这会强制当前线程等待,直到某个其他线程在同一个对象上调用notify()或notifyAll()。
为此,当前线程必须拥有对象的监视器。根据Javadocs的说法,这可能发生在:
我们已经为给定对象执行了同步实例方法
我们在给定对象上执行了synchronized块的主体
通过为Class类型的对象执行同步静态方法
请注意,一次只有一个活动线程可以拥有对象的监视器。
这个wait()方法带有三个重载签名。我们来看看这些。
(1) wait()
该wait()方法导致当前线程无限期地等待,直到另一个线程要么调用notify()此对象或notifyAll()
(2)wait(long timeout)
使用此方法,我们可以指定一个超时,在该超时之后将自动唤醒线程。可以使用notify()或notifyAll()在达到超时之前唤醒线程。
请注意,调用wait(0)与调用wait()相同。
(3) wait(long timeout,int nanos)
这是另一个提供相同功能的签名,唯一的区别是这个可以提供更高的精度。
总超时时间(以纳秒为单位)计算为1_000_000 *timeout+ nanos。
notify()和notifyAll()
该notify()方法用于唤醒正在等待到该对象的监视器接入线程。
有两种方法可以通知等待线程。
notify()
对于在此对象的监视器上等待的所有线程(通过使用任何一个wait()方法),方法notify()通知任何一个线程任意唤醒。确切唤醒哪个线程的选择是非确定性的 ,取决于实现。
由于notify()唤醒了一个随机线程,因此它可用于实现线程执行类似任务的互斥锁定,但在大多数情况下,实现notifyAll()会更可行。
notifyAll()
此方法只是唤醒正在此对象的监视器上等待的所有线程。
唤醒的线程将以通常的方式完成 - 就像任何其他线程一样。
但是在我们允许它们继续执行之前,总是要定义快速检查继续执行线程所需的条件 - 因为可能存在某些情况下线程被唤醒而没有收到通知(这种情况将在后面的示例中讨论)
用下鸡蛋的实例来理解一下
public class Egg {
boolean hasEgg = false;
Object obj = new Object();
//捡鸡蛋
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (true){
if(hasEgg){//有鸡蛋
System.out.println("捡到鸡蛋!!");
hasEgg=false;
}else {//没有鸡蛋
synchronized (obj){
System.out.println("等待另一个线程!!!");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
});
Thread t1 =new Thread(new Runnable() {
@Override
public void run() {
while (true){
try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("有鸡蛋了!!!!");
//通知捡鸡蛋线程工作
hasEgg=true;
synchronized (obj){
obj.notify();
}
}
}
});
}
public class test {
public static void main(String[] args) {
Egg e =new Egg();
e.t.start();
e.t1.start();
}
}
测试结果
等待另一个线程!!!
有鸡蛋了!!!!
捡到鸡蛋!!
等待另一个线程!!!
有鸡蛋了!!!!
捡到鸡蛋!!
等待另一个线程!!!