线程同步锁

线程同步锁

sychronized关键字: 把有可能出现问题的代码包起来,一次只让一个线程执行。通过sychronized关键字实现同步。当多个对象操作共享数据时,可以使用同步锁解决线程安全问题。

1. 格式:

synchronized(对象){
    // 需要同步的
}

1.1 注意点:

  • 锁的位置: 不能太大, 会降低效率. 也不能太小, 相当于没锁住. 所以要找一个合适的位置
  • 锁的对象: 要求多个线程间使用的是同一把锁

1.2 使用范围:

可以修饰方法, 也可以用在代码块上

2. 概念

2.1 原理

是指给你的共享资源加锁, 还是可以让多个线程操作共享资源, 只不过哪个线程有钥匙, 拿着钥匙进来开锁使用共享资源, 没有钥匙的线程等待.

2.2 同步和异步的区别:

  • 同步: 是需要拿着钥匙开锁, 同一时刻只能有一个线程共享资源, 其他线程排队. 牺牲了效率, 提高了安全
  • 异步: 是没有排队的线程, 大家同时使用了共享资源. 对数据不安全, 效率高

2.3 同步锁的特点:

  1. 前提1,同步需要两个或者两个以上的线程。
  2. 前提2,多个线程间必须使用同一个锁。
  3. 同步的缺点是会降低程序的执行效率, 为了保证线程安全,必须牺牲性能。
  4. 可以修饰方法称为同步方法,使用的锁对象是this。
  5. 可以修饰代码块称为同步代码块,锁对象可以任意。

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修饰后,那么该方法一定具有同步效果.

如果需要锁静态资源, 则所对象应该写: 类名.class

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锁
posted @ 2020-05-23 18:10  zpk-aaron  阅读(758)  评论(1编辑  收藏  举报