线程同步
1、关于多线程并发环境下,数据的安全问题是重点。
在实际开发中,项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现。这些代码我们都不需要编写。
最重要的是:程序需要放到一个多线程的环境下运行,更需要关注的是这些数据在多线程并发的环境下是否是安全的。
2、满足以下3个条件,就会存在线程安全问题。
- 条件1:多线程并发。
- 条件2:有共享数据。
- 条件3:共享数据有修改的行为。
用排队执行解决线程安全问题。这种机制被称为:线程同步机制。实际上就是线程不能并发了,线程必须排队执行。
线程排队会牺牲一部分效率,但数据安全是第一位,只有数据安全,才可以谈效率。
异步编程模型:多个线程各自执行各自的,谁也不需要等谁,这种编程模型叫做:异步编程模型。其实就是:多线程并发(效率较高。)
同步编程模型:多个线程不能同时进行,必须等待一个线程结束,另一个线程才能执行。两个线程之间发生了等待关系,其实就是:排队执行(效率较低)。
Synchronized关键字
1、Java中三大变量:实例变量(在堆中)、静态变量(在方法区)、局部变量(在栈中)。
- 局部变量和常量永远不会有线程安全问题。
- 成员变量:可能会有线程安全问题。
2、线程同步机制的语法是:
- 第一种:
synchronized(多个线程共享的){
// 线程同步代码块。
}
synchronized后面小括号中传的这个数据必须是多线程共享的数据。才能达到多线程排队。
需要找对象锁,一个对象只有一把锁。每个对象都有自己的锁。
/*取钱的方法*/
public void Withdraw(double money) {
synchronized (this) {
/*读取当前余额*/
double before = getBalance();
/*取钱*/
double after = getBalance() - money;
/*模拟网络延迟*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*取钱后修改当前余额*/
setBalance(after);
}
}
- 第二种:Synchronized 出现在实例方法中
synchronized出现在实例方法上,一定锁的是this。只能是this,不能是其他的对象了。所以这种方式不灵活。
另外还有一个缺点:synchronized出现在实例方法上,表示同步整个方法体,可能会无故扩大同步的范围,导致程序的执行效率降低。所以这种方式不通用。
使用场景:如果共享的对象就是this,并且需要同步的代码块是整个方法体,建议使用这种方式。
/*取钱的方法*/
public synchronized void Withdraw(double money) {
/*读取当前余额*/
double before = getBalance();
/*取钱*/
double after = getBalance() - money;
/*模拟网络延迟*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*取钱后修改当前余额*/
setBalance(after);
}
- 第三种:在静态方法上使用synchronized
需要找类锁。类锁永远只有1把,即使创建了100个对象,类锁也只有一把。
public synchronized static void Withdraw(double money) {
}
3、死锁
synchronized在开发中最好不要嵌套使用,容易发生死锁现象。
public class DeadLockTest {
public static void main(String[] args) {
/*创建Object对象*/
Object o1 = new Object();
Object o2 = new Object();
/*创建线程对象*/
MyThread01 t1 = new MyThread01(o1,o2);
MyThread01 t2 = new MyThread01(o1,o2);
/*启动线程*/
t1.start();
t2.start();
}
}
class MyThread01 extends Thread {
Object o1;
Object o2;
public MyThread01(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
}
}
}
}
class MyThread02 extends Thread {
Object o1;
Object o2;
public MyThread02(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
}
}
}
}