ReentrantLock、synchronized、volatile

synchronized

把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性和 可见性。原子性意味着一个线程一次只能执行由一把锁保护的代码,从而防止多个线程在更新共享状态时相互冲突。原子操作表示不可被中断的一个或一系列操作。
java 中的每一个对象都可以作为锁。具体表现为一下形式:
1、对于普通同步方法,锁是当前实例对象。
2、对于静态同步方法,锁是当前类的class对象。
3、对于同步方法块,所示synchronized括号里配置的对象。
当一个线程访问同步代码块时,他必须首先得到锁,退出或抛出异常时必须释放锁。

Lock 和synchronized 

lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!

public class T{
	private int count = 10;
	public synchronized void m() { 
		count--;
		System.out.println(Thread.currentThread().getName() + " count = " + count);
	}
	public static void main(String[] args) {
		T t = new T();
		for(int i=0; i<5; i++) {
			new Thread(t::m, "THREAD" + i).start();
		}
	}
}

同步与非同步方法是可以同时调用的,两个同步方法也是可以同时调用的,但是后一个必须等待前一个执行完成才能拿到第二把锁

public class T {
	public synchronized void m1() { 
		System.out.println(Thread.currentThread().getName() + " m1 start...");
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " m1 end");
	}
	public void m2() {
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " m2 ");
	}
	public static void main(String[] args) {
		T t = new T();
		new Thread(t::m1, "t1").start();
		new Thread(t::m2, "t2").start();
	}
}

synchronized获得的锁是可重入的,也就是说,一个同步方法可以调用另外一个同步方法,因为一个线程已经获取对象锁,再次申请的时候还是可以获取到该对象锁

ReentrantLock 

多线程都在争用同一个锁时,使用 ReentrantLock 的总体开支通常要比 synchronized 少得多。

用 ReentrantLock 保护代码块时,一定要使用finally块关闭锁。

public class ReentrantLock2 {
	Lock lock = new ReentrantLock();

	private void m1() {
		try {
			lock.lock(); //相当于synchronized(this)
			for (int i = 0; i < 10; i++) {
				TimeUnit.SECONDS.sleep(1);
				System.out.println(i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	void m2() {
		lock.lock();
		System.out.println("m2 ...");
		lock.unlock();
	}

	public static void main(String[] args) {
		ReentrantLock2 rl = new ReentrantLock2();
		new Thread(rl::m1).start();
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		new Thread(rl::m2).start();
	}
}

以上m1()执行完毕后才会执行m2()。m1()必须手动释放锁,否则m2()永远不会执行。ReentrantLock也可以指定为公平锁,线程调度器计算哪个线程等待的时间比较长,会让它先执行,所以不是绝对公平:

public class ReenLock {
	static Lock lock = new ReentrantLock(true);
	private void m3() {
		for (int i = 0; i < 100; i++) {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + " 获得锁");
			lock.unlock();
		}
	}

	public static void main(String[] args) {
		ReenLock l = new ReenLock();
		new Thread(l::m3).start();
		new Thread(l::m3).start();
	}
}

volatile

java允许多线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该通过互斥单独获得这个变量。如果一个变量被声明为volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。但它只是保证了可见性,不保证原子性,所以会结合锁一起使用。

jdk1.8后synchronized已经优化,但是还是比volatile重。synchronized是一种悲观锁,一个线程访问某个资源时,它会悲观的认为其他线程也会来访问该资源,java有个乐观锁CAS(Compare And Swap),其设计思想是乐观地认为不会产生并发,所以不上锁,在取数据的时候,判断一下在此期间是否有人修改,如果没有修改,则直接使用。

public class Test {
	volatile boolean running = true;

	private void m() {
		System.out.println(Thread.currentThread().getName() + " m start..");
		while (running) {

		}
		System.out.println(Thread.currentThread().getName() + " m end..");
	}

	public static void main(String[] args) {
		Test t = new Test();
		new Thread(() -> t.m(), "t1").start();
		try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t.running = false;
	}
}

 

class LoanTask{
	static String LOAN_STATUS = "LOAN_STATUS";
}
class BorrowTask{
	public Object getLock() {
		return LoanTask.LOAN_STATUS;
	}

	Object lock = getLock();
	synchronized (lock) {
		lock.notifyAll();//必须先跟synchronized配合使用

	}
}

参考示例程序:lock与unlock

java内存模型

以下仅是做记录

ConcurrentUtil.register(user);
public void offer(T model) {
	if (!queue.contains(model)) {
		queue.offer(model);
		try {
			System.out.println("------------11111111--------");
			LOCK.lock();
			task.execute();
			System.out.println("------------33333333--------");
		}finally{
			LOCK.unlock();
			System.out.println("-----44444444444-------");
		}
	}
}

再研究下task.execute()这个方法。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我知道了。首先我需要解释一下 synchronized 关键字、volatileReentrantLock 类与阻塞队列 LinkedBlockingQueue<E> 的概念。 synchronized 关键字是 Java 中用于实现同步的关键字,可以用来修饰代码块、方法和静态方法,用于实现线程间的同步。 volatile 是一个关键字,用于修饰变量,用于实现多线程之间的可见性,即当一个线程修改了一个 volatile 变量的值时,其他线程可以立即看到这个修改。 ReentrantLock 类是一个可重入的互斥锁,可以用来实现线程间的同步,与 synchronized 关键字相比,ReentrantLock 提供了更多的功能,如公平锁和可中断锁等。 阻塞队列 LinkedBlockingQueue<E> 是一个基于链表实现的阻塞队列,在多线程编程中可以用来实现线程间的同步。 接下来,我将使用这些同步方法实现主线程与子线程的同步,并写出实验小结。 代码如下: ``` import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.locks.ReentrantLock; public class Main { private volatile boolean flag = false; private final ReentrantLock lock = new ReentrantLock(); private final LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(); public static void main(String[] args) { Main main = new Main(); main.start(); } private void start() { Thread t = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; lock.lock(); try { queue.put(1); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }); t.start(); lock.lock(); try { while (!flag) { Thread.sleep(100); } System.out.println(queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } ``` 在这个例子中,主线程和子线程都需要修改和访问共享变量 flag,因此需要使用 volatile 关键字来实现可见性。 主线程需要等待子线程修改 flag 后才能继续执行,因此可以使用 while 循环不断检查 flag 是否被修改,这里使用了 Thread.sleep(100) 来减少 CPU 的占用率。 子线程需要将一个元素加入到队列中,并通知主线程可以继续执行,因此可以使用 ReentrantLock 和 LinkedBlockingQueue 来实现线程间的同步。 实验小结: 在多线程编程中,同步是一个非常重要的概念,可以用来避免线程间的竞争和冲突。在 Java 中,可以使用 synchronized 关键字、volatileReentrantLock 类和阻塞队列 LinkedBlockingQueue<E> 等方法来实现线程间的同步。在实际编程中,应根据情况选择不同的同步方法,以保证程序的正确性和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值