(一)synchronize的用法:
可重入原理:加锁计数器:jvm负责跟踪对象被加锁的次数,线程第一个对象加锁时,计数为1,每当这个相同的线程在此对象再次获得锁时,计数会递增,当任务离开时,计数会递减,当计数为0时,锁被完全释放
保证可见性的原理,内存模型:JSR133定义的其中一条happen-before规则:对一个监视器的解锁操作happens-before于每一个后续对同一个监视器的加锁操作。
即当ThreadA释放锁时,它所写过的变量(比如,x和y,存在它工作内存中的)都会同步到主存中,而当ThreadB在申请同一个锁时,ThreadB的工作内存会被设置为无效,然后ThreadB会重新从主存中加载它要访问的变量到它的工作内存中(这时x=1,y=1,是ThreadA中修改过的最新的值)。通过这样的方式来实现ThreadA到ThreadB的线程间的通信。
Synchronize的缺点:
1.效率低:锁释放的情况少,试图获取锁时不能设定超时,不能中断一个正在试图获取锁的线程
2.不够灵活:加锁的时机单一,每个锁仅有单一的条件,可能是不够的
3.等待的线程不能退出,直至获取到锁
使用时的注意点:
1.锁对象不能为空:因为锁的信息都在对象头中记录,如果作为锁对象为null,对象头是没有信息的,这样的对象是不能作为锁的
2.作用域不宜过大:Synchronized的作用域过大,导致大部分代码都串行,效率很低
3.避免死锁
如何选择Synchronized和Lock?
能不用就都不用,尽量使用JUC包中提供的工具类,如果Synchronized适用,就用Synchronized,可以减少代码编写,也不会因为粗心而忘记锁释放的问题
多线程访问方法的具体情况:(https://blog.csdn.net/weixin_34247032/article/details/91314307)
1.两个线程访问一个对象的同步方法
package synchronize_volatile;
/*
* 两个线程访问一个对象的同步方法
* */
public class TestSynchronized1 implements Runnable{
public synchronized void methord(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
@Override
public void run() {
methord();
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
TestSynchronized1 t =new TestSynchronized1();
Thread thread = new Thread(t);
Thread thread1 = new Thread(t);
thread1.start();
thread.start();
while(thread.isAlive()||thread1.isAlive()){
}
long endTime = System.currentTimeMillis();
System.out.println("总共用时"+(endTime-startTime));
}
}
----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------
当前线程开始Thread-1
当前线程结束Thread-1
当前线程开始Thread-0
当前线程结束Thread-0
总共用时6000
----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------
分析,总用时6000,也就是说发生了等待,线程2等待线程1运行完才开始运行,所以总共用时6000
2.两个线程访问两个对象的同步方法
package synchronize_volatile;
/*
* 两个线程访问两个对象的同步方法
* */
public class TestSynchronized1 implements Runnable{
@Override
public void run() {
synchronized (this) {
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
TestSynchronized1 t =new TestSynchronized1();
TestSynchronized1 t1 =new TestSynchronized1();
Thread thread = new Thread(t);
Thread thread1 = new Thread(t1);
thread1.start();
thread.start();
while(thread.isAlive()||thread1.isAlive()){
}
while(thread.isAlive()||thread1.isAlive()){
}
long endTime = System.currentTimeMillis();
System.out.println("总共用时"+(endTime-startTime));
}
}
----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------
当前线程开始Thread-1
当前线程开始Thread-0
当前线程结束Thread-1
当前线程结束Thread-0
总共用时3002
----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------
多次运行测试后,发现结果大于等于3000,两个线程之间不存在等待关系,并行处理不受干扰,锁的不是同一个对象
3.两个线程访问两个个对象的静态同步方法
package synchronize_volatile;
/*
* 两个线程访问两个个对象的静态同步方法
* */
public class TestSynchronized3 implements Runnable{
public static synchronized void methord(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
@Override
public void run() {
methord();
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
TestSynchronized3 t =new TestSynchronized3();
Thread thread = new Thread(t);
Thread thread1 = new Thread(t);
thread1.start();
thread.start();
while(thread.isAlive()||thread1.isAlive()){
}
long endTime = System.currentTimeMillis();
System.out.println("总共用时"+(endTime-startTime));
}
}
----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------
当前线程开始Thread-1
当前线程结束Thread-1
当前线程开始Thread-0
当前线程结束Thread-0
总共用时6003
----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------
多次运行测试后,发现结果大于等于6000线程之间不存在等待关系,对应的锁是同一把
4. 两个线程访问一个对象的同步方法和非同步方法
package synchronize_volatile;
/*
* 两个线程访问一个对象的同步方法和非同步方法
* */
public class TestSynchronized4 implements Runnable{
public synchronized void methord(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
public void methord1(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
@Override
public void run() {
if("Thread-0".equals(Thread.currentThread().getName())){
methord();
}else{
methord1();
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
TestSynchronized4 t =new TestSynchronized4();
Thread thread = new Thread(t);
Thread thread1 = new Thread(t);
thread1.start();
thread.start();
while(thread.isAlive()||thread1.isAlive()){
}
long endTime = System.currentTimeMillis();
System.out.println("总共用时"+(endTime-startTime));
}
}
----------------------------------------------------------------------------------------------------------分割线,运行结果----------------------------------------------------------------------------------------------
当前线程开始Thread-1
当前线程开始Thread-0
当前线程结束Thread-1
当前线程结束Thread-0
总共用时3000
----------------------------------------------------------------------------------------------------------分割线,结果分析----------------------------------------------------------------------------------------------
非同步方法不受影响
5. 两个线程访问一个对象两个不同的同步方法
package synchronize_volatile;
/*
* 两个线程访问一个对象两个不同的同步方法
* */
public class TestSynchronized5 implements Runnable{
public synchronized void methord(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
public synchronized void methord1(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
@Override
public void run() {
if("Thread-0".equals(Thread.currentThread().getName())){
methord();
}else{
methord1();
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
TestSynchronized5 t =new TestSynchronized5();
Thread thread = new Thread(t);
Thread thread1 = new Thread(t);
thread1.start();
thread.start();
while(thread.isAlive()||thread1.isAlive()){
}
long endTime = System.currentTimeMillis();
System.out.println("总共用时"+(endTime-startTime));
}
}
6.两个线程访问一个对象的同步方法和静态同步方法
package synchronize_volatile;
/*
* 两个线程访问一个对象的同步方法和静态同步方法
* */
public class TestSynchronized5 implements Runnable{
public synchronized void methord(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
public synchronized static void methord1(){
System.out.println("当前线程开始"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("当前线程结束"+Thread.currentThread().getName());
}
@Override
public void run() {
if("Thread-0".equals(Thread.currentThread().getName())){
methord();
}else{
methord1();
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
TestSynchronized5 t =new TestSynchronized5();
Thread thread = new Thread(t);
Thread thread1 = new Thread(t);
thread1.start();
thread.start();
while(thread.isAlive()||thread1.isAlive()){
}
long endTime = System.currentTimeMillis();
System.out.println("总共用时"+(endTime-startTime));
}
}
7.抛出异常,锁会被释放0)
https://blog.csdn.net/u012723673/article/details/80682208
(二)volatile
volatile特性:保证可见性,不保证原子性,volatile修饰的关键字能避免重排序
(三)区别:
1.volatile本质是下、在高速jvm当前变量咋寄存器当中的值是不确定的,需要从主存中读取,synchronizated则是锁住当前变量,只有当前线程可以访问该变量时,其他线程被阻塞
2.volatile只能使用在变量级别,而synchronizated可以使用在变量,方法和类级别
3.volatile可以改变线程的可见性,而Synchronized可以保证线程的可见性和原子性
4.volatile不会造成线程阻塞,而Synchronized会造成线程阻塞
5.volatile标记的变量不会被编译器优化,而Synchronized会被编译器优化
6.当一个域的值依赖他之前的值得时候volatile就无法工作,例如i++