小编给大家分享一下Java中锁的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Java中的锁Java中的加锁操作有两种: 1.synchronized锁(
小编给大家分享一下Java中锁的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
Java中的加锁操作有两种:
在操作系统的层面使用的是互斥锁(mutex lock)
在Java中放在了对象头中。
操作锁的流程
尝试获取锁
使用锁
释放锁
package ThreadDeom;class Counter2 { private static volatile int count = 0; public void increase() { for (int i = 0; i < 10000; i++) { count++; } } public void decrease() { for (int i = 0; i < 10000; i++) { count--; } } public int getCount() { return count; }}public class ThreadDemo19 { public static void main(String[] args) throws InterruptedException { //声明锁对象,任何的对象都可以作为锁 Object lock = new Object(); Counter2 counter2 = new Counter2(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { //使用锁 synchronized (lock) { counter2.decrease(); } } }); Thread thread2 = new Thread(() -> { synchronized (lock) { counter2.increase(); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(counter2.getCount()); }}
结果是:
public class ThreadDemo19 { public static void main(String[] args) throws InterruptedException { //声明锁对象,任何的对象都可以作为锁 Object lock = new Object(); Counter2 counter2 = new Counter2(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { //使用锁 synchronized (lock) { counter2.decrease(); } } }); Thread thread2 = new Thread(() -> { synchronized (lock) { counter2.increase(); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(counter2.getCount()); }}
package ThreadDeom;class Counter1 { private static volatile int count = 0; public void increase() { for (int i = 0; i < 10000; i++) { count++; } } public void decrease() { for (int i = 0; i < 10000; i++) { count--; } } public int getCount() { return count; }}public class ThreadDemo18 { public static void main(String[] args) throws InterruptedException { Counter1 counter1 = new Counter1(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { counter1.decrease(); } }); Thread thread2 = new Thread(() -> { counter1.increase(); }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(counter1.getCount()); }}
package ThreadDeom;public class ThreadDemo20 { private static int num = 0; private static final int maxSize = 100000; public static void main(String[] args) throws InterruptedException { ThreadDemo20 threadDemo20 = new ThreadDemo20(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { threadDemo20.increase(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { threadDemo20. decrease(); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(num); } //给静态的方法进行加锁,被加的锁是当前的对象。// public synchronized static void increase(){ //给普通的方法进行加锁的操作 public synchronized void increase() { for (int i = 0; i < maxSize; i++) { num++; } } // public synchronized static void decrease(){ public synchronized void decrease() { for (int i = 0; i < maxSize; i++) { num--; } }}
Lock类的使用
也叫手动锁
package ThreadDeom;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ThreadDemo22 { private static int number = 0; private static final int maxSize = 100000; public static void main(String[] args) { //创建lock锁对象,lock是接口,不能实列化 Lock lock = new ReentrantLock(); Thread thread1 = new Thread(() -> { for (int i = 0; i < maxSize; i++) { lock.lock(); try { number++; } finally { lock.unlock(); } } }); Thread thread2 = new Thread(() -> { for (int i = 0; i < maxSize; i++) { lock.lock(); try { number--; } finally { lock.unlock(); } } }); System.out.println(number); }}
lock()操作一定要放在try外面
如果放在try的里面:
try中抛出了异常,还没有加锁就释放了finally中的锁的操作了
如果放在了try,没加锁就释放了锁,就会抛出异常,就会将业务代码中的异常吞噬掉?如果一定要放的话,将lock()放在try的第一行。
package ThreadDeom;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ThreadDemo23 { public static void main(String[] args) { Lock lock = new ReentrantLock(); try{ System.out.println(1/0); lock.lock(); } finally { lock.unlock(); } }}
公平锁的调度:
一个线程释放锁。
主动唤醒“需要得到锁”的队列来得到锁。
非公平锁
当一个线程释放锁之后,另一个线程刚好执行到获取锁的代码就可以直接获取锁。
Java中的所有锁默认都是非公平锁。
非公平锁的性能更高。
ReentrantLock可以设置非公平锁。
公平锁
package ThreadDeom;import java.util.concurrent.locks.ReentrantLock;public class ThreadDemo24 { public static void main(String[] args) throws InterruptedException { ReentrantLock reentrantLock = new ReentrantLock(); Thread thread1 = new Thread(() -> { for (int i = 0; i < 100; i++) { reentrantLock.lock(); try { System.out.println("thread1"); } finally { reentrantLock.unlock(); } } }); Thread thread2 = new Thread(() -> { for (int i = 0; i < 100; i++) { reentrantLock.lock(); try { System.out.println("thread2"); } finally { reentrantLock.unlock(); } } }); Thread.sleep(100); thread1.start(); thread2.start(); }}
打印的结果是无序的
如果设置为公平锁:?
thread1和thread2 交替输出
synchronzied可以自动的进行加锁和释放锁,而Lock需要手动的加锁、释放锁。
Lock是Java层面的锁实现,而synchronzied 是JVM层面锁的实现
synchronzed 即可以修饰代码块,又可以修饰普通方法和静态的方法,而Lock 只能修饰代码块
synchronized 实现的是 非公平的锁,而Lock 可以实现公平锁。
lock的灵活性更高
在两个或两个以上的线程运行中,因为资源的抢占而造成线程一直等待的问题。看?:
package ThreadDeom;public class ThreadDemo25 { public static void main(String[] args) throws InterruptedException { Object lockA = new Object(); Object lockB = new Object(); Thread thread1 = new Thread(() -> { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "获取到lockA"); //让线程2获取lockB try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "获取到lockB"); } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { //线程2获取资源B synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "获取到lockB"); //让线程1先获取到锁lockA try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "获取到lockA"); } } } }); thread1.start(); thread2.start(); }}
这就造成了死锁
1.互斥条件:
当资源被一个线程拥有之后,就不能被其它的线程拥有了
2.拥有请求条件:
当一个线程拥有了一个资源之后,又试图请求另一个资源。
3.不可剥夺条件:
当一个线程拥有了一个资源之后,如果不是这个线程主动的释放资源,其他线程就不能拥有这个线程。
4.环路等待条件:
两个或两个以上的线程拥有了资源之后,试图获取对方的资源的时候形成了一个环路。
解决请求拥有和环路等待。
最有效的解决方案就是控制加锁的顺序。
package ThreadDeom;public class ThreadDemo26 { public static void main(String[] args) throws InterruptedException { Object lockA = new Object(); Object lockB = new Object(); Thread thread1 = new Thread(() -> { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "获取到lockA"); //让线程2获取lockB try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "获取到lockB"); } } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "获取到lockA"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "获取到lockB"); } } } }); thread1.start(); thread2.start(); }}
线程之间的通讯是指在一个线程中的操作可以影响另一个线程。
拥有相同锁的线程之间才能使用wait/notify机制。
wait()是Object()的方法,它的作用是是当前执行wait()方法的线程等待,在wati()所在的代码出停止执行,并释放锁,直到接到通知或者被中断为止。即在调用wait()的方法之前,线程必需先获取到对象级别的锁,也就是只能在同步方法或者同步块中使用wait()方法。
如果在使用wait()方法之前线程没有获得相应的锁,那么程序在执行时就会抛出异常。
notify()方法要在同步方法或者同步块中执行,即在调用notify()方法之前,线程必需要先获取到锁对象。如果线程没有持有锁对象的话,那么也会抛出异常。该方法用来通知可能在等待该锁的其它线程,如果有多个线程,那么则按照执行wait()方法的顺序来对处于wait()方法的线程发出通知,并使该线程重新获取锁。执行notify()方法之后,当前线程不会马上释放锁,处于wait()状态的线程也不会立马得到这个对象锁。而是要等notify的synchronized同步区域执行完成之后才会释放锁,处于wait()状态的线程才会得到锁对象。
总结:wait()方法用于让线程停止运行,而notify()方法用于通知暂停的线程继续运行。
在使用wait()或者notify()方法之前没有对象锁,就会报异常?:
lock.notify();
正确的使用之后
package ThreadDeom;public class ThreadDemo27 { //设置锁对象 private static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("在wait()"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("被notify()唤醒之后"); } } }); thread.start(); Thread.sleep(1000); synchronized (lock) { lock.notify(); } }}
注意:使用wait()方法的时候一定要和线程的锁对象是一个锁。
在多线程的情况下使用notify()方法只可以唤醒一个线程?
package ThreadDeom;public class ThreadDemo28 { //设置锁对象 private static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("thread1在wait()"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread1被notify()唤醒之后"); } } }); Thread thread2 = new Thread(() -> { synchronized (lock) { System.out.println("thread2在wait()"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread2被notify()唤醒之后"); } }); Thread thread3 = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { System.out.println("thread3在wait()"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("thread3被notify()唤醒之后"); } } }); thread1.start(); thread2.start(); thread3.start(); Thread.sleep(1000); synchronized (lock) { System.out.println("主线程调用notify()之后"); lock.notify(); } }}
那么如果使用notifyAll()方法呢?
可以看到所有的线程都被唤醒了
那么使用notify()唤醒的线程有没有什么顺序呢?
使用notify()唤醒线程的顺序是正序、倒序、还是随机的,这取决与JVM的具体实现,并不是所有的JVM在执行notify()时都是按照wait()的执行顺序进行唤醒的,也不是所有的notidyAll()都是按照wait()方法的倒序进行唤醒的,这取决于JVM的具体实现。
wait()和notify()不能唤醒指定的线程。
也可以让wait()等待指定的时间,如果超过给定的时间,wait()不会无限期的等待下去.
没有被notify()唤醒,过了1000毫秒之后会自动停止。
wait()在不传入任何参数的时候,线程会进入waiting 的状态,而在wait()中加入一个大于0的参数的时候,线程会进入time_wating的状态。
sleep()和wait()的区别 : 线程在sleep()的时候是不会释放锁的,而执行wait()的时候它就会释放锁。?:
package ThreadDeom;import jdk.nashorn.internal.ir.Block;public class ThreadDemo29 { private static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { synchronized (lock) { try { System.out.println("thread获取到了锁"); //如果sleep释放锁的话,会在thread获取到了锁和thread释放了锁之间打印 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("thread释放了锁"); } }); thread.start(); //让thread 先获取到锁 Thread.sleep(1000); synchronized (lock) { System.out.println("主线程获取到了锁"); } }}
可以看到线程在sleep()的时候,线程是不会释放锁的。再来看看wait()方法?:
wait()和sleep()都是让线程进行休眠的
wait()和sleep()方法都有可能在执行的过程接收到线程终止的通知
wait()必须和synchronzied一起使用,而sleep()不用。
wait()会释放锁,而sleep()不会释放锁。
wait()时Object的方法,而sleep()时Thread的方法。
默认情况下,wait()不传任何的参数的情况下,wait()会进入waiting的状态,如果传递了参数,wait()会进入time_waiting的状态。而sleep()进入的是time_waiting的状态。
sleep(0)表示0毫秒之后继续执行,而wait(0)表示线程会一直休眠下去wait(0)和wait()是一样的,wait()的源码就是调用了wait(0)方法。
sleep(0)表示重新出发一次CPU的竞争。
为什么wait()会释放锁,而sleep()不会释放锁?
sleep()需要传递一个最大的等待时间,也就是说sleep()是可控的,而wait()是不可以传递参数的,从设计的层面来说,如果让wait()一直持有所得话,那么线程就可能一直阻塞。
为什么wait()是Object的方法,而sleep()是线程的方法?
wait()需要操作锁,而锁是属于对象级别的,所有的锁都是放在对象头中的,它不是线程级别的,一个线程可以有多把的锁,为了灵活,就将wait()放在Object中了。
使用LockSupport可以解决wait()/notify()随机唤醒的问题。
package ThreadDeom;import java.util.concurrent.locks.LockSupport;public class ThreadDemo30 { public static void main(String[] args) { Thread thread1 = new Thread(new Runnable() { @Override public void run() { //让线程休眠 LockSupport.park(); System.out.println("unPark()了thread1"); } }); Thread thread2 = new Thread(() -> { LockSupport.park(); System.out.println("unPark()了thread2"); }); Thread thread3 = new Thread() { @Override public void run() { LockSupport.park(); System.out.println("unPark()了thread3"); } }; thread1.start(); thread2.start(); thread3.start(); LockSupport.unpark(thread1); LockSupport.unpark(thread2); }}
以上是“Java中锁的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!
--结束END--
本文标题: Java中锁的示例分析
本文链接: https://lsjlt.com/news/297059.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
2024-05-24
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0