同步锁
sychronized
关键字: 把有可能出现问题的代码包起来,一次只让一个线程执行。通过sychronized
关键字实现同步。当多个对象操作共享数据时,可以使用同步锁解决线程安全问题。
1. 格式:
synchronized(对象){
// 需要同步的
}
1.1 注意点:
- 锁的位置: 不能太大, 会降低效率. 也不能太小, 相当于没锁住. 所以要找一个合适的位置
- 锁的对象: 要求多个线程间使用的是同一把锁
1.2 使用范围:
可以修饰方法, 也可以用在代码块上
2. 概念
2.1 原理
是指给你的共享资源加锁, 还是可以让多个线程操作共享资源, 只不过哪个线程有钥匙, 拿着钥匙进来开锁使用共享资源, 没有钥匙的线程等待.
2.2 同步和异步的区别:
- 同步: 是需要拿着钥匙开锁, 同一时刻只能有一个线程共享资源, 其他线程排队. 牺牲了效率, 提高了安全
- 异步: 是没有排队的线程, 大家同时使用了共享资源. 对数据不安全, 效率高
2.3 同步锁的特点:
- 前提1,同步需要两个或者两个以上的线程。
- 前提2,多个线程间必须使用同一个锁。
- 同步的缺点是会降低程序的执行效率, 为了保证线程安全,必须牺牲性能。
- 可以修饰方法称为同步方法,使用的锁对象是this。
- 可以修饰代码块称为同步代码块,锁对象可以任意。
3. 使用案例
注意点:
- 可以把有想成安全隐患的的代码锁起来-- 同步的锁代码块
- 锁的位置: 要合理, 不嫩太大也不能太小. 建议从共享资源开始位置一直到使用刚结束都锁起来
- 锁的对象: 代码块的锁对象可以是任意的对象, 只要是多个线程间同一个对象就可以. 也可以是字符串
3.1 同步代码块案例
售票案例:
// 这个类用来模拟多线程售票
public class Test1_Tickets2 {
public static void main(String[] args) {
MyTickets target = new MyTickets();
// 第一个参数是绑定的Runnable实现类对象, 第二个参数是线程的名称
Thread t1 = new Thread(target, "窗口1");
Thread t2 = new Thread(target, "窗口2");
Thread t3 = new Thread(target, "窗口3");
Thread t4 = new Thread(target, "窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
// 创建类实现接口
class MyTickets implements Runnable {
// 定义变量, 记录票数
int tickets = 100;
// 写业务
@Override
public void run() {
while (true) { // 一直卖票
synchronized ("锁") { //括号中的对象必须是多个线程公共的一个对象
if (tickets > 0) { // 有票就卖
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印线程名称和票数
System.out.println(Thread.currentThread().getName() + " : " + tickets --);
} else { // 没票就停
break;
}
}
}
}
}
加了线程锁之后, 就不会出现超卖和重卖的情况了
3.2 方法中使用
当一个方法被synchronized修饰后,那么该方法称为同步方法,即:多个线程不能同时在方法内部执行,从而解决了并发的安全问题.
在方法上使用synchronize,那么同步监听器对象是当前方法所属对象,即:方法内部看到的this
public synchronizd int getBean() {
return new Integer(0);
}
静态方法若使用synchronize修饰后,那么该方法一定具有同步效果.
4. 死锁现象 (了解)
当多个线程都持有自己的锁,但是都等对方先释放锁时就会出现"僵持"的情况,使得所有线程进入阻塞状态.这个现象,称为死锁.
死锁代码案例:
public class Dead_Syn {
public static void main(String[] args) {
Test t = new Test();
new Thread(){
public void run() { // A线程
t.methodA();
}
}.start();
new Thread(){
public void run() { // B线程
t.methodB();
}
}.start();
}
}
class Test {
private Object oa = new Object(); // A锁
private Object ob = new Object(); // B锁
public void methodA() {
System.out.println("得到oa锁");
synchronized (oa) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A方法-oa锁内部");
methodB(); // 调用B方法
}
System.out.println("释放oa锁");
}
public void methodB() {
System.out.println("得到ob锁");
synchronized (ob) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B方法-ob锁内部");
methodA(); // 调用A方法
}
System.out.println("释放ob锁");
}
}
控制台输出 (输出后程序一直没有结束, 锁一直没有释放!)
得到oa锁
得到ob锁
B方法-ob锁内部
A方法-oa锁内部
得到oa锁
得到ob锁