美文网首页
java基本知识之协程

java基本知识之协程

作者: 瀚海网虫 | 来源:发表于2019-12-30 15:27 被阅读0次

Java 支持多个线程同时访问一个对象或者对象的成员变量,关键字
synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线
程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量
访问的可见性和排他性,又称为内置锁机制。
对象锁和类锁:
对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态
方法或者一个类的 class 对象上的。我们知道,类的对象实例可以有很多个,但
是每个类只有一个 class 对象,所以不同对象实例的对象锁是互不干扰的,但是
每个类只有一个类锁。
但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存
在的,类锁其实锁的是每个类的对应的 class 对象。类锁和对象锁之间也是互不
干扰的

1. 一般协作

wait()
wait(long) 等待xx毫秒后,无通知就返回超时
wait(long,int) 更精细的超时控制,单位纳秒
notify() 有可能死锁
notifyAll() 不会死锁,尽量使用notifyAll()

2. ThreadLocal

ThreadLocal,即线程变量,是一个以 ThreadLocal 对象为键、任意对象为值
的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个
ThreadLocal 对象查询到绑定在这个线程上的一个值, ThreadLocal 往往用来实现
变量在线程之间的隔离。

public final static ThreadLocal<String> RESOURCE = new
ThreadLocal<String>();RESOURCE代表一个能够存放String类型的ThreadLocal对象。
此时不论什么一个线程能够并发访问这个变量,对它进行写入、读取操作,都是
线程安全的。

3. synchronized 使用场景

  1. 方法同步
    public synchronized void xxx(){}

  2. 代码块同步
    synchronized(this){}

锁对象:

  1. 类级锁
    synchronized(A.class){}

  2. 对象所
    Object o = new Object();
    synchronized(o){}

实例
电影票售卖,剩余总票数5票,由5个地方都可以发起售票。

public class MTickets implements Runnable {
   private int index;
    public MTickets(int index) {
        this.index = index;
    }
    @Override
    public void run() {
        while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//                synchronized (this) {
                    if (index > 0 ){
                    System.out.println(Thread.currentThread().getName() + " 线程卖出了票" + index--);
                }
//            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        int index = 5;
        MTickets t = new MTickets(index);
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        Thread t4 = new Thread(t);
        Thread t5 = new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}
Thread-3 线程卖出了票5
Thread-2 线程卖出了票4
Thread-4 线程卖出了票2
Thread-1 线程卖出了票3
Thread-0 线程卖出了票5
Thread-4 线程卖出了票0
Thread-0 线程卖出了票-1
Thread-2 线程卖出了票1
Thread-1 线程卖出了票-2
Thread-3 线程卖出了票-3

可以看出逻辑明显出了问题

引入synchronized,解决

 @Override
    public void run() {
        while (true) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (this) {
                    if (index > 0 ){
                    System.out.println(Thread.currentThread().getName() + " 线程卖出了票" + index--);
                }
            }
        }
    }

输出结果

Thread-0 线程卖出了票5
Thread-3 线程卖出了票4
Thread-4 线程卖出了票3
Thread-2 线程卖出了票2
Thread-1 线程卖出了票1

4. Lock

4.1 Lock 与Synchronized 区别

我们一般的 Java 程序是靠 synchronized 关键字实现锁功能的,使用
synchronized 关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就
是先获取再释放。synchronized 属于 Java 语言层面的锁,也被称之为内置锁。
synchronized 这种机制,一旦开始获取锁,是不能中断的,也不提供尝试获
取锁的机制。
而 Lock 是由 Java 在语法层面提供的,锁的获取和释放需要我们明显的去获
取,因此被称为显式锁。并且提供了 synchronized 不提供的机制。

4.2 可重入锁 ReentrantLock、所谓锁的公平和非公平

synchronized 关键字隐式的支持重进入,比如一个 synchronized 修饰的递
归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁。
ReentrantLock 在调用 lock()方法时,已经获取到锁的线程,能够再次调用 lock()
方法获取锁而不被阻塞。

公平和非公平锁
如果在时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,
反之,是不公平的。公平的获取锁,也就是等待时间最长的线程最优先获取锁,
也可以说锁获取是顺序的。

4.3 读写锁 ReentrantReadWriteLock

之前提到锁(synchronized 和 ReentrantLock)基本都是排他锁,这些锁在同
一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,
但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对
锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁
有了很大提升。
除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化
读写交互场景的编程方式。假设在程序中定义一个共享的用作缓存数据结构,它
大部分时间提供读服务(例如查询和搜索),而写操作占有的时间很少,但是写
操作完成之后的更新需要对后续的读服务可见。
一般情况下,读写锁的性能都会比排它锁好,因为大多数场景读是多于写的。
在读多于写的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量

相关文章

网友评论

      本文标题:java基本知识之协程

      本文链接:https://www.haomeiwen.com/subject/zjnpoctx.html