Java多线程锁---synchronized

Java中常用的线程操作方法

取得当前JVM中正在执行的线程对象 

public static native THread currentThread();

线程名称的命名和取得 

  • public Thread(Runnable target, String name) 
  • public final synchronized void setName(String name)     
  • String getName()

线程休眠(线程暂缓执行一下

立即交出CPU,不会释放对象锁    sleep(long time)

线程让步(暂停当前正在执行的线程对象,让拥有相同优先级的线程获取CPU)

不会立即交出CPU,不会释放对象锁   yield() 

线程等待(在线程中加入一个线程,本线程阻塞,当加入的线程执行完,本线程恢复执行)

join()

线程停止

  1. 设置标志位(无法处理线程阻塞时停止的问题)
  2. 调用Thread类提供的stop方法强行关闭线程,但是会产生不完整数据
  3. 调用Thread类提供的interrupt():
  • 若线程中没有使用类似sleep/wait/join时,调用此线程对象的interrupt方法并不会真正中断线程,只是简单地将线程的状态置为interrupt而已,根据此状态来进一步确定如何处理线程。
  • 若线程中调用了阻塞线程的方法如:sleep()、wait()、join()此时再调用线程的interrupt方法时会抛出异常InterruptedException,同时将线程状态还原(isInterrupted = false)

Thread类提供的public boolean isInterrupted()可以检测当前线程状态是否为中断状态

线程优先级(线程优先级越高越有可能先执行,仅此而已)

  1. 设置优先级setPriority(int priority)
  2. 取得优先级int getPriority()

JDK内置了三种优先级

  • MAX_PRIORITY = 10;
  • NORM_PRIORITY = 5;
  • MIN_PRIORITY = 1;

线程的继承性:
若在一个线程中创建了子线程,默认子线程与父线程的优先级相同。

守护线程(只要JVM存在任何一个用户线程没有终止,守护线程就一直在工作)

将线程对象设置为守护线程  setDaemon(true)

用户线程,默认创建的线程,包括主线程

守护线程:垃圾回收线程

线程的同步与死锁

同步:线程间通信(线程合作)

互斥:多线程并发时,某一时刻只能有一个线程访问共享资源

一把锁保护一个相应资源,不同锁保护不同的对象/资源

锁的实现

使用synchronized关键字为程序逻辑上锁

synchronized的两种用法

  • 同步代码块:synchronized(锁的对象|Object及其子类|类对象) {}
  • 同步方法:public [static] synchronized method(){}

synchronized的应用

synchronized(this){} 锁的是当前对象,在任意时刻,只能有一个线程操作此对象,对别的对象不影响

synchronized(类对象) {} 锁的是当前类.class,在任意时刻,只能有一个线程操作当前类的一个对象,影响其他对象

synchronized修饰普通方法,锁当前对象,

synchronized修饰静态方法,锁当前类.class

如何保护毫无关系的资源?使用多把锁锁住不同的资源

class Account {
    // 余额
	int sal;
    // 密码
    String password;
    // 余额资源的锁
    private Object salLock = new Object();
    // 密码资源的锁
    private Object passLock = new Object();
    public int getMoney() {
    	synchorinzed(salLock) {}
    }

    public void setMoney() {
    	synchorinzed(salLock) {}
    }

    public String getPassword() {
    	synchorinzed(passLock) {}
    }
    
    public void setPassword() {
    	synchorinzed(passLock) {}
    }
}

如何保护有关联关系的对象(使用同一把锁):

public void zhuangzhang(Account target) {
	synchroinzed(Account.class) {
		this.sal -= 100;
		target.sal += 100;
	}
}

synchronized底层实现:

两个命令:    编译命令:javac -encoding utf-8 主类名称

                      反编译命令:javap -v 主类名称

对象锁Monitor机制

monitorenter:

  • 检查对象的Monitor计数器值是否为0,为0表示此监视器还未被任意一个线程获取,此时线程可以进入同步代码块并且将Monitor值+1,将Monitor的持有线程标记为当前线程
  • 当Monitor计数器值不为0且持有线程不是当前线程,表示Monitor已经被别的线程占用,当前线程只能阻塞等待。
  • (可重入锁:)当执行MonitorEnter时。对象的Monitor计数器值不为0,但是持有线程恰好是当前线程,此时将Monitor计数器值再次+1,当前线程继续进入同步方法或代码块

monitorexit

  • 当执行MonitorEnter时。对象的Monitor计数器值不为0,但是持有线程恰好是当前线程,此时将Monitor计数器值再次+1,当前线程继续进入同步方法或代码块

之所以采用这种计数器的方式,是为了同一个线程重复获取同一把锁,如果Java类中拥有多个synchronized方法,那么这些方法之间的相互调用,不管是直接还是间接的,都会涉及对同一把锁的重复加锁操作,因此,我们需要设计这个一个可重入的特性,来避免编程里的隐式约束

synchronized优化(JDK1.6之后)

主要思想:让每个线程通过同步代码块时速度提高,之前synchronized如果获取锁失败,就会将线程挂起(悲观锁策略),但线程启动需要占用大量资源

CAS操作(CompareAndSwap(V,O,N))

V:内存中该变量的具体值 

O:当前线程存储的变量值 
N:希望修改后的变量值 

已知内存中的旧值,多个线程同时操作来修改他的值,若O==V则证明没有线程修改他的值,则直接修改为N,返回N,在接下来的线程尝试修改时,内存中的值已被修改为N,此时O!=V,返回内存中的最新值V。多个线程再次尝试修改变量(乐观锁)

存在的问题以及解决方式

1.ABA问题:添加版本号

2.自旋在CPU上跑无用指令,会浪费CPU资源:自适应式自旋

3.公平性问题(处于阻塞的线程可能无法获得到锁):lock锁实现公平锁

偏向锁:JDK1.6之后默认synchronized

最乐观的锁:进入同步块或同步方法的始终是一个线程

当出现另一个线程也尝试获取锁(在不同时刻)时,偏向锁会升级为轻量级锁

轻量级锁

不同时刻有不同的线程尝试获取锁,"亮黄灯策略"

同一时刻有不同线程尝试获取锁,会将偏向锁升级为重量锁

重量级锁

JDK1.6之前synchronized都是重量级锁,将线程阻塞挂起(JDK1.6自适应自旋)

锁只有升级过程没有降级过程

锁粗化

当出现多次连续的加锁与解锁过程,会将多次加减锁过程粗化为一次的加锁与解锁过程(StringBuffer)

锁消除

当对象不属于共享资源时,对象内部的同步方法或同步代码块的锁会被自动解除(StringBuffer)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值