锁的类型
1.对象锁
在方法上加的锁,都是对象锁,一旦一个线程进入某个被锁的方法,其他线程也不能进入其他枷锁的方法
class Phone{
public synchronized void sendEmail(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("-----sendEail");
}
public synchronized void sendSMS(){
System.out.println("-----sendSMS");
}
}
类锁
对静态方法加锁是类所
类锁锁的事整个类,即该类new出来的其他对象都是持有相同的锁
public synchronized static void sendSMS(){
System.out.println("-----sendSMS");
}
对于同步代码块,锁的事同步代码块内的内容
public void sayHello(){
synchronized (this){
System.out.println("sayhelloo");
}
}
对于synchronized 方法,经过反编译后发现,方法的flag上面有
ACC_SYNCHRONIZED进行修饰
对于下面代码,我们进行javap -v 反编译看看有啥区别。
对于每一个synchronized 包裹的方法,都对应一个 monitorenter
2个monitorexit,分别对应加锁和释放锁动作
public class LockSyncDemo {
Object object = new Object();
public synchronized void m1(){
synchronized (object){
System.out.println("---hello synchronized code hello block");
}
}
public static void main(String[] args) {
}
}
public synchronized void m1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: getfield #3 // Field object:Ljava/lang/Object;
4: dup
5: astore_1
6: monitorenter
7: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
10: ldc #5 // String ---hello synchronized code hello block
12: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15: aload_1
16: monitorexit
17: goto 25
20: astore_2
21: aload_1
22: monitorexit
23: aload_2
24: athrow
25: return
Exception table:
公平锁和非公平锁
公平锁 是指多个线程按照申请锁的顺序来获取锁。这里类似于排队的方式,获得锁的机会均等
非公平锁是,是指的多个线程获取锁的顺序并不是按照申请锁的顺序,有个后申请锁的顺序的线程优先或许锁。
ReentrantLock
ReentrantLock lock = new ReentrantLock(true);
上面方法,如果不传入true则创建的非公平锁
public class SaleTicketDemo {
static Ticket ticket = new Ticket();
public static void main(String[] args) {
new Thread(()->{
for (int i = 0;i<55;i++)
ticket.sale();
},"售货员A").start();
new Thread(()->{
for (int i = 0;i<55;i++)
ticket.sale();
},"售货员B").start();
new Thread(()->{
for (int i = 0;i<55;i++)
ticket.sale();
},"售货员C").start();
}
}
class Ticket //资源类,模拟3个售票员,买完50张票
{
ReentrantLock lock = new ReentrantLock(true);
private int number = 50;
public void sale(){
lock.lock();
try {
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出第:\t"+(number--)+"\t 还剩下:"+number);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
这个传入true,是公平锁,从执行结果我们也可以看出
可冲入锁
可冲入锁,又名递归锁。一个线程,多次进入自己持有的同一把锁,不会发生死锁
死锁
死锁是两个或者两个以上的线程在执行过程争夺资源而造成一种相互等待的现象。
public class DeadLock {
public static void main(String[] args) {
final Object objectA = new Object();
final Object objectB = new Object();
new Thread(()->{
synchronized (objectA){
System.out.println(Thread.currentThread().getName()+"\t 自己持有A锁,希望释放获得B锁");
try {
TimeUnit.SECONDS.sleep(1);
synchronized (objectB){
System.out.println(Thread.currentThread().getName()+ "\t 成功的获得了B锁");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"A").start();
new Thread(()->{
synchronized (objectB){
System.out.println(Thread.currentThread().getName()+"\t 自己持有A锁,希望释放获得B锁");
try {
TimeUnit.SECONDS.sleep(1);
synchronized (objectA){
System.out.println(Thread.currentThread().getName()+ "\t 成功的获得了B锁");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"B").start();
}
}
比如代码中A线程持有A锁,希望获得B锁,B锁又被B线程持有,B锁又希望或者A,他们都获得不得想要的锁,只能等待了。
如何查看死锁呢
在终端输入jps -l 找到进程号,然后输入jstack 17303,这里17303就是进程号码,如下的代码中就提示发现了死锁
Java stack information for the threads listed above:
===================================================
"B":
at com.keelon.zsimall.usedmall.demo.DeadLock.lambda$main$1(DeadLock.java:31)
- waiting to lock <0x000000076ae0d430> (a java.lang.Object)
- locked <0x000000076ae0d440> (a java.lang.Object)
at com.keelon.zsimall.usedmall.demo.DeadLock$$Lambda$2/33524623.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"A":
at com.keelon.zsimall.usedmall.demo.DeadLock.lambda$main$0(DeadLock.java:17)
- waiting to lock <0x000000076ae0d440> (a java.lang.Object)
- locked <0x000000076ae0d430> (a java.lang.Object)
at com.keelon.zsimall.usedmall.demo.DeadLock$$Lambda$1/959447386.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
Monitor 管程
指针指向monitor对象(也成为管程或者监视器)的起始地址,每个object对象都有一个monitor对象与之关联,当一个monitor被某个线程持有后,变处于锁定的状态,在java虚拟机中monitor对象是有ObjectMonitor实现的
线程终端机制
线程的中断机制一般通过协商中断来实现的。
通过volatile来实现线程中断
public class interruptdemo {
static volatile boolean isStop = false;
public static void main(String[] args) {
new Thread(()->{
while (true){
if (isStop){
System.out.println("isStop被修改为true程序停止");
break;
}
System.out.println("t1---hello volatile");
}
},"ti").start();
try {
TimeUnit.MICROSECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
isStop = true;
},"t2").start();
}
}
上面的代码,通过另外一个线程设置volatle修饰的变量。从而实现t1线程的中断.
通过AtomicBoolean来实现线程中断
/**
* 通过原子boole实现线程的中断
*/
public class AtomicBooleanDemo {
static AtomicBoolean isStop = new AtomicBoolean(false);
public static void main(String[] args) {
new Thread(()->{
while (true){
if (isStop.get()){
System.out.println("isStop被修改为true程序停止");
break;
}
System.out.println("t1---hello atomicbool");
}
},"ti").start();
try {
TimeUnit.MICROSECONDS.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
isStop.set(true);
},"t2").start();
}
}
interrupt 中断标识
public class interuptcode {
public static void main(String[] args) {
//实例方法interrupt(),仅仅是设置线程的中断状态位置为true,不会立即停止线程
Thread t1 = new Thread(()->{
for (int i = 0 ;i<300;i++){
System.out.println("---:\t"+i);
}
},"t1");
t1.start();
try {
TimeUnit.MILLISECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t1.interrupt();
System.out.println("t1线程调用interupte()的中断标识"+t1.isInterrupted());
}
}
1.实例方法interrupt(),仅仅是设置线程的中断状态位置为true,不会立即停止线程
2.如果线程处于被阻塞的状态(例如处于sleep,wait,join等状态中),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞的状态,并抛出一个interruptedException异常
public class interruptDemo3 {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
while (true){
if(Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"\t"+"中断标志位:"+Thread.currentThread().isInterrupted()+"程序停止");
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("---hello--interruptDemo3");
}},"t1");
t1.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->t1.interrupt()).start();
}
}
3.静态方法interupted,第一次调用会返回当前线程的interrupted的状态,第二次调用会重置interrupted的标志位
线程的等待和唤醒 LookSupport
线程的等待唤醒synchronized,wait,nofify
wait进入等待是时候,释放所持有的锁,等待其他线程notify唤醒
public class LockSupportDemo {
public static void main(String[] args) {
Object objectLock = new Object();
new Thread(()->{
synchronized (objectLock){
System.out.println(Thread.currentThread().getName()+"\t ---come in");
}
System.out.println(Thread.currentThread().getName()+"\t ---被唤醒");
},"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(()->{
synchronized (objectLock){
objectLock.notify();
System.out.println(Thread.currentThread().getName()+"\t---发出通知");
}
}).start();
}
}