线程的锁机制

锁机制的介绍

针对于临界资源安全隐患问题的解决方式。引入锁机制。 1. 锁机制的作用:将异步的代码块变成同步的代码块。 2. 语法: synchronized(锁对象的地址){ //需要同步的代码块( 如果不同步,就会出现安全隐患问题) } 3. 任何的java对象都可以作为锁。 一个要求: 所有的线程看到的都是同一个对象。 4. 同步的代码块在可能的情况下,尽量缩小范围,提高其他代码的并发效率。 5. 运行逻辑: 当一个线程A执行到{}里,就表示该线程获取了锁对象,其他线程都必须等待,直到 线程A执行完了同步代码块,会自动释放锁对象。其他线程才有机会获取锁对象,谁获取到锁 对象,谁就执行同步代码块。

package CSDN.day02;

public class _13 {
    public static void main(String[] args) {

    }
}

class Desk1 implements Runnable {
    private static int beanCount = 10;

    public void take() {
        beanCount--;
    }

    public void run() {
        while (true) {


            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (this) {
                if (beanCount > 0) {
                    take();
                }
            }
            System.out.println(Thread.currentThread().getName() + "拿走了一颗豆子,剩余:" + beanCount);
            if (beanCount <= 0) {
                break;
            }
        }
    }
}

synchronized的作用域

1.如果使用了synchronized(锁对象){ } ,那么{ }中就是同步块。

2.synchronized可以作用在非静态方法,此时锁对象是this。

3.synchronized可以作用在静态方法上,此时锁对象是类名.class。

package CSDN.day0821;

public class _01 {
    public static void main(String[] args) {

        Blackboard b1 = new Blackboard();
        Thread xm = new Thread(b1,"小明");
        Thread xl = new Thread(b1,"小莉");
        xm.start();
        xl.start();
    }
}
class  Blackboard implements Runnable{
    private  static int apple = 10;
    public void run() {
        while(true){
            take();
            System.out.println(Thread.currentThread().getName()
            +"拿走了一颗苹果,还剩"+apple+"个苹果");
            if(apple <= 0){
                break;
            }
        }
    }
    public synchronized void take(){
        if(apple > 0){
            apple--;
        }
    }
}
4.synchronized可以锁住方法里面的部分代码
    即锁的范围就是同步块的范围
    此时,锁对象可以是java的任何引用类型的对象。
5.非静态方法上锁: 就是在方法上添加修饰词synchronized
-1. 当一个线程访问了该方法时,就是获取了锁对象,其他线程想要访问该方法,处于等待状态。
-2. 有个前提:多个线程访问的对象实例必须是同一个。 比如下面案例的桌子 必须是同一张桌子。
-3. 当一个线程正在访问一个实例的某一个同步方法时,this这个锁对象即被他所占用,其他线程想要执行
 该实例的其他的同步方法时,也需要等待,因为this锁已经被占用了。
package CSDN.day0821;

public class _02 {
    public static void main(String[] args) {

        Desk desk1 = new Desk();
        Desk desk2 = new Desk();
        Thread t1 = new Thread(desk1,"xm");
        Thread t2 = new Thread(desk1,"qq");
        t1.start();
        t2.start();
    }
}
class  Desk implements Runnable{
    private int BeanCount = 10;

    public synchronized void take(){
        System.out.println("开始取豆子");
        if(BeanCount>0){
            BeanCount--;
        }
        if(BeanCount==0){
            System.out.println("没豆子了");
        }
//        System.out.println("豆-1");

    }
    public void run() {
        while(true){
            take();
            if (BeanCount<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"取豆,还剩:"+BeanCount);

        }
    }

}

单例设计懒汉模式的锁

package CSDN.day0821;

import java.util.LinkedList;
import java.util.List;

public class _03 {
    public static void main(String[] args) {
        List pool = new LinkedList<>();
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(){
                public void run(){
                    Boss b = Boss.getInstance();
                }
            };
            pool.add(t);
        }
        for (Thread t : pool) {
            t.start();
        }
    }
}

class Boss {
    private static Boss instance;

    private Boss() {
    }

    public synchronized static Boss getInstance() {
        //synchronized(Boss.class)
        if (instance == null) {
            instance = new Boss();
        }
        return instance;
    }
}

死锁

产生原因:线程1 先获取锁A,然后再想要获取锁B 

线程2 先获取锁B,然后再想要获取锁A

 两个线程都占用了对方想要的锁,而对方还占用并不释放。因此都出现了等待现象,无法继续向下执行。 这就是死锁。

例如:

package CSDN.day0821;

public class _04 {
    public static void main(String[] args) {
        Thread t1 = new Thread("小明"){
            public void run(){
                synchronized ("A"){
                    for(int i=1;i<=10;i++){
                        System.out.println(getName()+":"+i);
                    }
                }
                synchronized ("B"){
                    for(int i=1;i<=10;i++){
                    System.out.println(getName()+":"+i);
                }}

            }
        };
        Thread t2 = new Thread("小l"){
            public void run(){
                synchronized ("B"){
                    for(int i=1;i<=10;i++){
                        System.out.println(getName()+":"+i);
                    }
                }
                synchronized ("A"){
                    for(int i=1;i<=10;i++){
                        System.out.println(getName()+":"+i);
                    }}

            }
        };
    }
}

避免死锁方法

 1.  就是按照顺序加锁
 2.  设置超时等待。 设置了一定时间限制,如果在这个时间范围内没有获取到锁,那就不执行锁里的内容

package CSDN.day0821;

public class _05 {
    public static void main(String[] args) {
        Thread t1 = new Thread("小明"){
            public void run() {
                synchronized ("A"){
                    for (int i = 0; i < 50; i++) {
                        System.out.println(getName()+":"+i);
                    }
                    synchronized ("B"){
                        for (int i = 50; i < 100; i++) {
                            System.out.println(getName()+":"+i);
                        }
                    }
                }
            }
        };
        Thread t2 = new Thread("小红"){
            public void run() {
                synchronized ("B"){
                    for (int i = 0; i < 50; i++) {
                        System.out.println(getName()+":"+i);

                    }
                    synchronized ("A"){
                        for (int i = 50; i < 100; i++) {
                            System.out.println(getName()+":"+i);
                        }
                        try {
                            "B".wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        };
        t1.start();
        t2.start();
    }
}
与锁有关的方法

1、wait():释放自己占有的锁对象,进入等待队列中,不参与锁的争抢,不参与时间片段的争抢。 也就是阻塞了。知道被notify/notifyAll.注意,是锁调用该方法

wait(long timeout):等待一个指定时间,如果超过这个时间,则自动唤醒。

wait(long timeout ,intnaous) 指定的等待时间更精确一些。

2.notify():通知,唤醒等待队列中的某一个线程,是随机的。被唤醒的那个线程进入锁池状态,开始争抢锁对象。
3. notifyAll():通知,唤醒等待队列中的所有线程。被唤醒的所有线程进入锁池状态,开始争抢锁对象。
package CSDN.day0822;

public class _01 {
    public static void main(String[] args) {
        Object obj = new Object();
        Thread down = new Thread(() -> {
            System.out.println("开始加载图片");
            for (int i = 1; i <= 100; i++) {
                System.out.println(Thread.currentThread()
                        .getName() + ": " + i + "%");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace( );
                }
            }
            System.out.println("图片加载完成");
            synchronized (obj) {
                obj.notify();
            }
        }, "下载图片");

        Thread show = new Thread(() -> {
            try {
                synchronized (obj) {
                    obj.wait(5000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("开始显示图片");
            for (int i = 1; i <= 100; i++) {
                System.out.println(Thread.currentThread()
                        .getName() + ": " + i + "%");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("图片显示完成");
            }

        }, "显示图片");
        down.start();
        show.start();
    }

   

}

可重入锁

标准写法:  释放锁操作在finally模块中
package CSDN.day0822;

import java.util.concurrent.locks.ReentrantLock;

public class _02 {
    public static void main(String[] args) {
        MyCounter1 counter = new MyCounter1("秒表");
        Thread thread = new Thread(counter,"壮壮");
        Thread thread1 = new Thread(counter,"十八");
        thread.start();
        thread1.start();
    }
}
class  MyCounter1 implements Runnable{
    private int count = 0;
    private String name;
    ReentrantLock rl = new ReentrantLock(true);
    public MyCounter1(String name) {
        this.name = name;
    }
    public void run() {
        rl.lock();
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()
            +"计数:"+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        try {
            String str = null;
            System.out.println(str.length());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rl.unlock();
        }
    }

}
是一个能让线程多次获取锁对象的类型,里面内置了一个计数器,用于记录当前线程获取该锁的次数 能避免死锁情况
该类提供了两个子类型:
非公平锁 :多个线程获取锁的方式不是按照线程的请求顺序,而是可能发生”插队“现象,这种锁性能高,但是可能增加线程解现象
公平锁:按照请求顺序获取,谁都获取到锁,减少了线程接线箱,但是系统的吞吐量可能不高
(性能高的线程也需要派对才能获取锁)
如何使用:  构造器
构造器中传入true,表示传入公平锁,传入false或者不指定参数,使用非公平锁
该类比synchronized更加灵活,但是需要手动上锁和解锁
lock()    上锁方法:锁对象没有被其他线程占用是,会成功,苟泽当前的线程处于阻塞状态
unlock()   必须在占有锁的时候才能进行解锁,否则报异常
trylock()   尝试获取锁,如果获取不到,不阻塞,而是执行其他代码
获取不到锁返回false 获取到返回true
trylock(long time,TimeUnit unit)  可以指定一定时间内获取锁对象,如果超市还没获取,返回false  获取返回true
package CSDN.day0822;

import java.util.concurrent.locks.ReentrantLock;

public class _03 {
    public static void main(String[] args) {
        MyCounter counter = new MyCounter("秒表");
        Thread t1 = new Thread(counter,"小明");
        Thread t2 = new Thread(counter,"小美");
        t1.start();
        t2.start();
    }
}
class MyCounter implements Runnable{
    private int count = 0;
    private String name;
    ReentrantLock rl = new ReentrantLock(true);
    public MyCounter(String name) {
        this.name = name;
    }
    public void run() {
        rl.lock();
        for (int i = 0; i < 10; i++) {
            count++;
            System.out.println(Thread.currentThread().getName()
            + ": " + count + " times");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        rl.unlock();
    }
}

你可能感兴趣的:(java,jvm,开发语言)