@[toc]
1.为什么要使用synchronized
在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。
案例
package com.yxl.demo.ThreadTest;
public class test5 {
public static void main(String[] args) {
TestDemo thread = new TestDemo();
Thread t1 = new Thread(thread,"窗口一");
Thread t2 = new Thread(thread,"窗口二");
t1.start();
t2.start();
}
}
class TestDemo implements Runnable{
//共享的火车票变量
private volatile int count = 100;
//重寫run方法
@Override
public void run() {
while (count > 0){
try {
Thread.sleep(50);
}catch (Exception e){
e.getMessage();
}
sale();
}
}
public void sale(){
if(count > 0){
System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));
count--;
}
}
}
会出现火车票卖重复的问题,
2.实现原理
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性
Synchronized的作用主要有三个:
原子性:确保线程互斥的访问同步代码;
可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的 “对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;
有序性:有效解决重排序问题
3.synchronized的三种应用方式
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
public synchronized void sale(){
if(count > 0){
System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));
count--;
}
}
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
public static synchronized void sale(){
if(count > 0){
System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));
count--;
}
}
同步代码块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
private Object object =new Object();
public void sale(){
synchronized (object){
if(count > 0){
System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));
count--;
}
}
}
synchronized jdk版本
在jdk1.6之前,synchronized是非常重量级的,因为它会无时无刻进行锁住对象,而不考虑到程序实际的竞争情况,大多数程序在都是进行交替执行,也就是说不存在资源的竞争,如果没有竞争,但是加锁,加锁和解锁是非常耗费性能的,(重量级)因为线程之间的切换以及线程从内核态到用户态的时间是耗费性能的。
在jdk1.6之后,sun公司对synchronized进行了大幅度的优化,现在采用偏向锁+(轻量级锁+cas)+重量级,之间通过锁碰撞进行切换。
后面对于锁新出文章进行讲解
synchronized的可重入性
从互斥锁的设计上来说,当一个线程试图操作一个由其他线程持有的对象锁的临界资源时,将会处于阻塞状态,但当一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功,在java中synchronized是基于原子性的内部锁机制,是可重入的,因此在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的,这就是synchronized的可重入性