java 总结几种线程异步转同步的方法

在做一款app的时候,用到了一个异步执行的api,而我想要的是同步执行,查了一些资料,对几种情况下的线程同步做一下总结。


一、共享资源的同步

问题:当一个资源被多个线程访问会发生错误,只能允许一个线程访问时。

1.syschronized实现

使用syschonized关键字可对某个块或者方法进行限制访问,即当一个线程获得块或者方法的访问权后,其他线程将不能访问。

public class synchronize_test implements Runnable{
	int num1 = 10;
	int num2 = 10;
	int num3 = 10;
	
	public void synchonized_test() {
		synchronized(this) {
			while(num1 > 0) { //只有得到锁的线程才能访问
				System.out.println(Thread.currentThread().getName() + "访问num1=" + num1--);
			}
		}
		synchronized(this) { //只有得到锁的线程才能访问,即使它还没有开始访问这儿,因为同步锁的作用对象是对象中的所有同步块
			while(num2 > 0) {
				System.out.println(Thread.currentThread().getName() + "访问num2=" + num2--);
			}
		}
		while(num3 > 0) { //未得到锁的线程可访问此资源(非同步块)
			System.out.println(Thread.currentThread().getName() + "访问num3=" + num3--);
		}
		
		
	}
	@Override
	public void run() {
		synchonized_test();
		
	}




	public static void main(String[] args) {
		synchronize_test sys = new synchronize_test();
		Thread t1 = new Thread(sys);
		Thread t2 = new Thread(sys);
		t1.start();
		t2.start();


	}
}


当使用sychronized修饰某个方法(非static)时,作用对象将是这个方法所属的对象,与同步块同理。而如果synchronized修饰的是static方法或变量时,作

用对象将是static所在的而非某个对象,因为static方法不属于任何一个对象,而是属于类。


2.Lock实现

可以看出,syschronized的作用的是对象或者类,这显然不太灵活,而Lock则比其更加灵活一些。

public class Lock_Test implements Runnable{
	int num1 = 10;
	int num2 = 10;
	Lock lock1 = new ReentrantLock();
	Lock lock2 = new ReentrantLock();
	
	public void synchonized_test() {
		lock1.lock(); //获得lock1
		try {
			while(num1 > 0) { //只有得到锁的线程才能访问
				System.out.println(Thread.currentThread().getName() + "访问num1=" + num1--);
			}
		} finally {
			lock1.unlock();
		}
		
		lock2.lock();//获得lock2
		try {
			while(num2 > 0) {
				System.out.println(Thread.currentThread().getName() + "访问num2=" + num2--);
			}
		} finally {
			//在finally中解锁以防死锁
			lock2.unlock(); //解锁
		}
		
	
		
	}
	
	@Override
	public void run() {
		synchonized_test();
		
	}


	public static void main(String[] args) {

		Lock_Test lt = new Lock_Test();
		Thread t1 = new Thread(lt);
		Thread t2 = new Thread(lt);
		t1.start();
		t2.start();

	}
}

这里使用的是ReentrantLock,另外还有ReadWriteLock。


ReentranLock的优点(摘自:https://github.com/pzxwhc/MineKnowContainer/issues/16)

lock在获取锁的过程可以被中断。

lock可以尝试获取锁,如果锁被其他线程持有,则返回 false,不会使当前线程休眠。

lock在尝试获取锁的时候,传入一个时间参数,如果在这个时间范围内,没有获得锁,那么就是终止请求。

synchronized 会自动释放锁,lock 则不会自动释放锁。


二、异步转同步

问题:某些API是异步的,而我们想让其同步。如:A、B两个方法异步执行,由于某些需求,想让A方法执行完之后再执行B方法。


1.CountDownLatch解决

使用CountDownLatch可以实现同步,它好比计数器,在实例CountDownLatch对象的时候传入数字,每使用一次 .countDown() 方法计数减1,当数字减到0时, .await()方法后的代码将可以执行,未到0之前将一直阻塞等待。

import java.util.concurrent.CountDownLatch;

public class CountDownLatch_test implements Runnable{
	private Integer num = null;
	
	private static CountDownLatch latch;
	
	public void setNumber() {
		num = 1;
	}
	
	public int getNumber() {
		return this.num;
	}

	@Override
	public void run() {
		if(Thread.currentThread().getName().equals("Thread-0")) { //t2线程
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			this.setNumber();
			latch.countDown(); //计数减1
		}
		else if(Thread.currentThread().getName().equals("Thread-1")){ //t1线程
			try {
				latch.await(); //阻塞等待计数为0
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("num = " + this.getNumber());
		}
		
	}

	public static void main(String[] args) {
		CountDownLatch_test c = new CountDownLatch_test();
		latch = new CountDownLatch(1);
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		 t1.start();
		 t2.start();

	}

}
如代码所示,t1线程获得num的值,t2线程给num赋值,显然t2需要在t1之前执行结束,而t2执行的时间却比t1长,故使用CountDown对t1进行阻塞等待t2完成。

此外,也可以给await(设置参数),到达一定时间计数未变为0也可执行。




********其他方法待学习*******

你可能感兴趣的:(java)