synchronized锁
为什么使用synchronized锁
如果多个线程对同一变量,并发访问修改,没有做同步处理最终就会导致出现脏读的问题。
非线程安全的变量
方法内的变量为线程安全的变量:jvm方法调用在操作数栈中,而操作数栈是每个线程独有的,线程间的栈是不共享的,所以方法内的变量是每个线程独有的,所以不会出现脏读(题外话:在单个线程间栈内创建数据如果存在值相同的,就指向同一个地址,就是栈共享)。
实例变量为非线程安全的变量:实例变量在实例化后将在堆上分配,而堆是线程间共享的,那么就会出现非线程安全的问题。
synchronized锁重入
- 可重入锁概念就是自己可以再次获取自己的内部锁。
public class MyService {public synchronized void method1() {
System.out.println("method1");
method2();
}public synchronized void method2() {
System.out.println("method2");
method3();
}public synchronized void method3() {
System.out.println("method3");
}
}public class ServiceThread extends Thread {@Override
public void run() {
MyService ms = new MyService();
ms.method1();
}
}public class Test {public static void main(String[] args) {
ServiceThread st = new ServiceThread();
st.start();
}
}
结果:
[Console output redirected to file:D:\console.txt]
method1
method2
method3
- 可重入锁支持父子继承关系中
public class Father {
public synchronized void execute1() {
System.out.println("Father execute1");
}
}public class Children extends Father{
public synchronized void execute2() {
System.out.println("Children execute2");
execute1();
}
}public class ServiceThread extends Thread {
@Override
public void run() {
Children c = new Children();
c.execute2();
}
}public class Test {public static void main(String[] args) {
ServiceThread st = new ServiceThread();
st.start();
}
}
结果:
[Console output redirected to file:D:\console.txt]
Children execute2
Father execute1
异常,锁自动释放
public class Children {public synchronized void execute2() {
System.out.println("Children execute2");
int i = 3 / 0;
System.out.println("i = " + i);
}
}public class ServiceThread extends Thread {@Override
public void run() {
Children c = new Children();
c.execute2();
}
}public class Test {public static void main(String[] args) {
ServiceThread st = new ServiceThread();
st.start();
}
}
结果:
[Console output redirected to file:D:\console.txt]
Children execute2
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at cn.spy.thread.test.Children.execute2(Children.java:8)
at cn.spy.thread.test.ServiceThread.run(ServiceThread.java:8)
synchronized同步代码块 【synchronized锁】synchronized方法对当前对象进行加锁,synchronized代码块是对某一个对象进行加锁。
1. 同步代码块和同步方法的区别
同步方法的特点就是只要是当前对象的同步方法,当前线程没执行完,其他线程只能等着。而同步代码块则是对于不同对象的同步代码块,线程1执行同步代码块1时,线程2也可以执行同步代码块2,这样效率会有所提升。而在我看来实际上两种方式只是细粒度上进一步得到了提升。同步代码块可以进行更加细微的操作。
2. synchronized(this)锁定当前对象
public class MyTask {public void execute() {
synchronized (this) {
for (int i = 0 ;
i < 3 ;
i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
}public class MyThreadA extends Thread{private MyTask myTask;
public MyThreadA(MyTask myTask) {
this.myTask = myTask;
}@Override
public void run() {
myTask.execute();
}
}public class MyThreadB extends Thread{private MyTask myTask;
public MyThreadB(MyTask myTask) {
this.myTask = myTask;
}@Override
public void run() {
myTask.execute();
}
}public class MyTest {public static void main(String[] args) {
MyTask myTask = new MyTask();
MyThreadA mtA = new MyThreadA(myTask);
mtA.setName("MyThreadA");
MyThreadB mtB = new MyThreadB(myTask);
mtB.setName("MyThreadB");
mtA.start();
mtB.start();
}
}
结果:
MyThreadA 0
MyThreadA 1
MyThreadA 2
MyThreadB 0
MyThreadB 1
MyThreadB 2
静态同步synchronized synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
public class MyTask {public synchronized void execute() {
System.out.println(Thread.currentThread().getName() + " execute start");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}System.out.println(Thread.currentThread().getName() + " execute end");
}public synchronized void print() {
System.out.println(Thread.currentThread().getName() + " print start");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}System.out.println(Thread.currentThread().getName() + " print end");
}
}public class MyThreadA extends Thread{private MyTask myTask;
public MyThreadA(MyTask myTask) {
this.myTask = myTask;
}@Override
public void run() {
myTask.execute();
}
}public class MyThreadB extends Thread{private MyTask myTask;
public MyThreadB(MyTask myTask) {
this.myTask = myTask;
}@Override
public void run() {
myTask.print();
}
}public class MyTest {public static void main(String[] args) {
MyTask myTask = new MyTask();
MyThreadA mtA = new MyThreadA(myTask);
mtA.setName("MyThreadA");
MyThreadB mtB = new MyThreadB(myTask);
mtB.setName("MyThreadB");
mtA.start();
mtB.start();
}
}
结果:
MyThreadA execute start
MyThreadA execute end
MyThreadB print start
MyThreadB print end
总结
- 方法内的变量是线程安全的变量,实例变量为非线程安全的变量。
- synchronized锁支持重重入。
- synchronized可重入锁支持父子继承关系中
- 遇到异常,synchronized锁自动释放。
- synchronized修饰方法意味着对当前调用方法的对象加锁同步。
- synchronized(this)锁定当前的对象。
- synchronized(class)锁定指定的类。
- synchronized修饰静态方法意味着给静态方法所在的类上锁。
推荐阅读
- 为什么你的路演总会超时()
- 财商智慧课(六)
- 吃了早餐,反而容易饿(为什么?)
- 为什么越花钱的人越有钱,越舍不得花钱的人却越穷()
- dubbo基本认识
- 为什么985/211的学生能胜任工作获得老板的青睐。
- 年轻人,干了孤独这杯酒
- 为什么孩子一定要学会可视化思维!
- 关于this的一些问题(1)
- 为什么有些女孩喜欢看玛丽苏爱情片()