一、同步机制
在多线程环境中,经常会遇到数据的共享问题,即当多个线程需要访问同一个资源时,它们需要以某种顺序来确保该资源在某一时刻只能被一个线程使用,否则,程序的运行结果将会不可预料(比如每个线程都有修改权限,同时改一个文件,有可能使一个人读取另一个人已经删除的内容,就会出错,同步就会要求按顺序来修改)
同步机制举例:多个线程同时对同一数据进行写操作,即当线程A需要使用某个资源时,如果这个资源正在被线程B使用,同步机制就会让线程A一直等待下去,直到线程B结束对资源的使用后(即需要等待),线程A才能使用这个资源。即同步机制能够保证资源的安全。
简单来说:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制,A等待
实现同步--锁:
要想实现同步操作,必须要或得每一个线程对象的锁。获得它可以保证在同一时刻只有一个线程能够进入临界区(访问互斥资源的代码块),并且在这个锁被释放前,其他线程就不能再进入这个临界区。若还有其他线程想要获得该对象的锁,只能进入等待队列等待。只有当拥有该对象锁的线程退出临界区,锁才能被释放,等待队列中优先级最高的获得锁。
public class Demo implements Runnable
{
private int tick=3;
public void run()
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+":"+tick--);
}
}
public static void main(String[] args)
{
Demo d=new Demo();
Thread thread1=new Thread(d);
Thread thread2=new Thread(d);
Thread thread3=new Thread(d);
Thread thread4=new Thread(d);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
Thread-1:3
Thread-0:0
Thread-3:1
Thread-2:2
出现这种结果的原因:tick是共享资源,当线程启动后,未加锁导致四个线程在共享资源>0时,均进入if里了。
解决方法:对共享资源顺序访问--同步 synchronized
//同步方式一:同步方法
public synchronized void run()
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+":"+tick--);
}
}
//同步方式二:同步代码块
public void run()
{
synchronized(this){
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+":"+tick--);
}
}
}
①被synchronized关键字修饰的代码块在被线程执行前,首先要拿到被同步对象的锁,并且一个对象只有一个锁,比如上面代码,首先拿到当前对象的锁,如果当前的锁被其他线程拿走了,那么还没抢到锁的线程将从可运行状态转换为阻塞状态,只有当拿到锁的线程执行完同步块的代码后,就释放锁,让给别的线程,这样就可以保证数据的完整性。
②线程同步不是说让一个线程执行完了再执行其他线程(同步方法是这样的),一般是让线程中的某一些操作(访问互斥资源)进行同步即可。
③synchronized是一种内置锁/监视器锁。java中每个对象都有一个内置锁,而synchronized就是使用对象的内置锁来把代码块锁定的
锁的两个主要特性:
1)互斥性:一次只允许一个线程持有某个特定的锁,因此可以使用此特性实现对共享数据的协调访问。这样,一次就 只有一个线程能够使用该共享数据。
2)可见性:释放锁之前对共享数据作出的更改该对于随后获得该锁的另一个线程是可见的
④java中同步机制中提供了语言级的支持,可以通过使用synchronized关键字来实现同步,但该方法并非“万金油”,它以很大的系统开销作为代价,有时甚至造成死锁。所以,同步控制并非越多越好,要尽量避免同步控制。
⑤同步与多线程关系:没多线程环境就不需要同步;有多线程环境也不一定要同步
总结:
为了防止线程并发对同一数据的修改,所以需要同步,否则会造成数据不一致(就是所谓的:线程安全,如java集合框架中Hashtable、Vector是线程安全的)
二、异步机制
A线程要请求某个资源,但是此资源正在被B线程使用中,因为没有同步机制存在,A线程仍然请求的到,A线程无需等待。
在进行输入输出时,不必关心其他线程的状态或行为,也不必等到输入输出处理完毕才返回。当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,异步能提高程序的效率。