(二)synchronized和重入锁
来源:互联网 发布:gif软件 电脑 编辑:程序博客网 时间:2024/06/05 02:52
程序运行起来一定要保证线程安全,所以在多线程中一定要对临界区资源加锁,synchronized和重入锁都可以用来加锁。
synchronized
用法
对对象加锁,进入同步代码块时需要获得对象的锁。
对实例方法加锁,相当于对当前实例加锁,进入代码块要获得当前实例对象的锁
对静态方法加锁,相当于对当前类加锁,进入代码块要获得对象的锁
注意
锁要加在对象上
锁不能加在基本数据类型上,因为java的自动拆装箱,也不要在基本类型包装类加锁,如当我们需要计数时,可能会想到给一个Integer对象加锁,但是Integer对象每改变一次引用就换掉了。结果就是等待的线程永远都唤不醒。若我A中的唤醒的操作和改变值得操作换一下,还会报错java.lang.IllegalMonitorStateException,表示我没有这个对象的锁,因为对象已近变了
public class Test1 { public static void main(String[] args) { new T1ThreadB().start(); new T1ThreadA().start(); }}class T1ThreadA extends Thread{ @Override public void run() { for(int i=0; i<10; i++) { synchronized (T1Data.count) { if(T1Data.count == 10) { T1Data.count.notifyAll(); } System.out.println("A加锁的对象" + T1Data.count.hashCode()); T1Data.count = T1Data.count + 1; } } }}class T1ThreadB extends Thread{ @Override public void run() { while(true) { synchronized (T1Data.count) { System.out.println("B加锁的对象" + T1Data.count.hashCode()); if(T1Data.count == 10) { break; }else { try { T1Data.count.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }}class T1Data{ public static Integer count = new Integer(0);}
尽量缩小加锁的范围,提高效率
当一个类全是静态方法时,最好不要直接对静态方法加锁,因为这样是把锁加在了这个类上,实例方法也是一样,若传的的是一个对象,效率也会低。若是方法是对一个数据进行操作,最好直接加锁那个对象而不是方法。缩小加锁的范围。
public class Test2 { public static void main(String[] args) {// new T2ThreadA().start();// new T2ThreadB().start(); new T2ThreadC().start(); new T2ThreadD().start(); }}class T2ThreadA extends Thread{ @Override public void run() { T2Util.test1(); }}class T2ThreadB extends Thread{ @Override public void run() { T2Util.test2(); }}class T2ThreadC extends Thread{ @Override public void run() { T2Util.test3(); }}class T2ThreadD extends Thread{ @Override public void run() { T2Util.test4(); }}class T2Util{ public synchronized static void test1() { System.out.println(Thread.currentThread().getName() + "访问test1"); try { Thread.currentThread().sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public synchronized static void test2() { System.out.println(Thread.currentThread().getName() + "访问test2"); try { Thread.currentThread().sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static LinkedList<String> list = new LinkedList<String>(); public static void test3() { synchronized (T2Util.list) { list.add("aaa"); System.out.println(Thread.currentThread().getName() + "访问test3"); } try { Thread.currentThread().sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void test4() { synchronized (T2Util.list) { list.add("aaa"); System.out.println(Thread.currentThread().getName() + "访问test4"); } try { Thread.currentThread().sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
wait和notify的注意事项
很有可能有这样一种情景,两个线程同时操作一个集合,一个向其中添加数据,一个取数据,获取数据为null时就等待,添加的发现大小为1就唤醒。获取的线程拿着获取的数据继续操作。但是要注意一点,线程wait 后会释放获得的锁,这点sleep就不会释放。当被唤醒后首先去竞争锁,得到锁后从等待的后面开始执行,唤醒后那个取得的数据还是null,若后面的操作没有判断可能就会出错。在使用线程池的时候等待最好设置一个等待最长时间,因为有线程池时要注意自己的逻辑,可能有些线程就一直等下去了,这样程序就不能进行了。只有两个线程就不用。
public class Test3 { public static void main(String[] args) { new T3ThreadA().start(); new T3ThreadB().start(); }}class T3ThreadA extends Thread{ @Override public void run() { try { Thread.currentThread().sleep(100);//为了让B有沉睡的可能 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(int i=0; i<20; i++) { synchronized (T3Data.list) { String s = new String("呵呵" + i); T3Data.list.add(s); if(T3Data.list.size() == 1) { T3Data.list.notifyAll(); } } } T3Data.flag = false; }}class T3ThreadB extends Thread{ @Override public void run() { boolean flag = true; while(flag) { String s = null; synchronized (T3Data.list) { System.out.println("B获得锁"); try { s = T3Data.list.getFirst(); T3Data.list.removeFirst(); }catch (Exception e) { } if(s == null) { if(T3Data.flag == false) { flag = T3Data.flag; }else { try { System.out.println("沉睡"); T3Data.list.wait(1000);//设置一个最长等待时间,有线程池时防止某个线程永远等下去,主要看自己的逻辑会不会出现这种情况。 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("被唤醒了"); } } } System.out.println("处理得到的数据" + s); } }}class T3Data{ public static LinkedList<String> list = new LinkedList<String>(); public static boolean flag = true;}
重入锁
用法
重入锁和synchronized是差不多的,但是功能更强大,和synchronized相比,重入锁有显示的操作,必须手动加锁释放锁。对逻辑控制的灵活性好于synchronized。重入锁可以中断响应、限时等待。
ReentrantLock几个重要的方法
lock():获得锁,如果锁已被占用,则等待
lockInterruptibly():获得锁,优先响应中断
tryLock():尝试获得锁,成功返回true,不等待
tryLock(long time,TimeUnit unit):在给定的时间尝试获得锁
unlock():释放锁
Condition几个方法
synchronized有Object.wait()等方法,Condition的方法和那几个方法类似
await():线程等待,当中断时跳出等待
signal():唤醒一个等待的线程
signalAll():唤醒所有等待的线程
awaitUninterruptibly():等待,但不响应中断
要注意的和操作方法都和synchronized差不多,下面列出一个重入锁的基本使用
public class Test4 { public static void main(String[] args) { new T4ThreadA().start(); new T4ThreadB().start(); }}class T4ThreadA extends Thread{ @Override public void run() { try { Thread.currentThread().sleep(100);//为了让B有沉睡的可能 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(int i=0; i<20; i++) { try { T4Data.lock.lock(); String s = new String("呵呵" + i); T4Data.list.add(s); if(T4Data.list.size() == 1) { T4Data.condition.signalAll(); } }catch (Exception e) { // TODO: handle exception }finally { T4Data.lock.unlock(); } } System.out.println("A完成"); T4Data.flag = false; }}class T4ThreadB extends Thread{ @Override public void run() { boolean flag = true; while(flag) { String s = null; try{ T4Data.lock.lock(); System.out.println("B获得锁"); try { s = T4Data.list.getFirst(); T4Data.list.removeFirst(); }catch (Exception e) { } if(s == null) { if(T4Data.flag == false) { flag = T4Data.flag; }else { System.out.println("沉睡"); T4Data.condition.await(10, TimeUnit.SECONDS); System.out.println("被唤醒了"); } } }catch (Exception e) { // TODO: handle exception }finally { T4Data.lock.lock(); } System.out.println("处理得到的数据" + s); } }}class T4Data{ public static ReentrantLock lock = new ReentrantLock(); public static Condition condition = lock.newCondition(); public static LinkedList<String> list = new LinkedList<String>(); public static boolean flag = true;}
- (二)synchronized和重入锁
- synchronized详解(二)
- java synchronized(二)
- 黑马程序员:synchronized和wait()、notify()的关系 (二)
- Java线程篇(二):线程同步(synchronized和volatile)
- Java线程(二)----线程同步synchronized和volatile
- synchronized修饰方法和修饰方法块(二)
- Java多线程(二):线程同步synchronized和volatile
- java synchronized详解(二)
- java synchronized 用法 (二)
- JAVA Synchronized 详解(二)
- 多线程(二):synchronized 关键字
- Synchronized和ReentrantLock重入锁
- 线程(二)线程同步synchronized和volatile
- HashMap,Hashtable,ConcurrentHashMap 和 synchronized Map 的原理和区别(二)
- java中的线程(二)——线程的同步和synchronized深入理解
- Java多线程总结(二):理解对象锁 & synchronized和Lock的区别
- Java synchronized同步线程机制(二)
- servlet的请求包含(request.getRequestDispatcher().include())
- JAVA实现二项队列
- loadrunner Lr_类函数之lr_db_connect()
- 使用lrzsz工具通过串口向开发板传送文件
- Understanding LSTM Networks
- (二)synchronized和重入锁
- apollo-server-koa 简单使用
- 机器学习 学习笔记
- 欢迎使用CSDN-markdown编辑器
- Search for a Range
- loadrunner Lr_类函数之lr_db_dataset_action()
- storm与hadoop对比
- loadrunner Lr_类函数之lr_db_disconnect()
- 无穷小微积分落地生根在中国的现实意义