synchroized是jvm提供的,无需手动控制它可以自动对多线程访问的资源进行加锁与解锁,它可以修饰方法,也可以修饰代码块.
这边博客记录下个人对synchrozied的理解如下:
1.为什么Java要提供synchronized?
当多条线程去对一个类的实例变量进行修改,这时会发生变量的不确定性.为解决这个问题,使用synchronized修饰,使得在某一时刻,只能有一条线程进行操作.实现的方式是加锁与解锁.
2.实例演示多线程访问共享变量产生的问题.
public class ManyThreadModify implements Runnable{
private int value = 10;
@Override
public void run() {
for(int i = 0; i < 10; i++) {
value--;
System.out.println(Thread.currentThread().getName() + "修改value的值为:" + value);
}
}
public static void main(String[] args) {
ManyThreadModify mm = new ManyThreadModify();
//使用实现Runnable接口的方式可以共享变量,每启动的线程都是以target形式启动的
new Thread(mm , "线程01").start();
new Thread(mm , "线程02").start();
}
}
可以看到,出现了value的值两次等于8的情况,这就是线程不安全造成的,在run方法上加上synchronize修饰可以解决这个问题.
3.synchrozied修饰方法和修饰代码块的区别
修饰方法:synchronize public void test(){}
修饰代码块:synchronize(锁对象){ //代码块 }
区别:
public class SynchronizedMethodAndCode{
//synchronize修饰方法
public synchronized void test01() {
try {
System.out.println("this is method test01...");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void test02() {
//synchronize修饰代码块,锁对象为当前对象
synchronized (this) {
System.out.println("this is method test02...");
}
}
public void test03() {
String str = "s";
//synchronize修饰代码块,锁对象为str
synchronized (str) {
System.out.println("this is method test03...");
}
}
}
启动线程访问
public class SynchroziedMain {
public static void main(String[] args) {
SynchronizedMethodAndCode sc = new SynchronizedMethodAndCode();
//启动线程1访问test01方法
new Thread(new Runnable() {
@Override
public void run() {
sc.test01();
}
}).start();
//启动线程2访问test02方法
new Thread(new Runnable() {
@Override
public void run() {
sc.test02();
}
}).start();
//启动线程3访问test03方法
new Thread(new Runnable() {
@Override
public void run() {
sc.test03();
}
}).start();
}
}
可以看到打印结果,
分析:启动线程1访问test01方法时遇到sleep(3000),线程1没有释放锁资源,在这里,锁资源就是当前对象,锁被线程1拿在了手里没有释放,而test02方法的锁对象this也是当前对象,所以线程1和线程2使用的是同一把锁,线程2等待线程1 sleep()结束才能获得锁,进入test02方法的打印代码中,所以线程3先打印了,因为它的锁是自己定义的一个String对象.
总结:synchronize修饰方法的锁对象只能是this当前对象
synchronize修饰代码块可以修改锁对象