线程 -- 01 -- 基础学习

121 阅读3分钟

学习笔记:

Java并发与线程基础学习

并发与并行 AND 多进程和多线程

并发:通过切换宏观上实现多个程序(任务)同时运行。相互之间抢占资源。

并行:同一时刻,多个程序(任务)同时进行。相互间不争抢资源

多进程:同一时刻CPU运行多个程序

多线程:单个程序完成多个任务,每个任务在一个线程中执行

同步

同一进程的多个线程是共享进程的资源,当多个线程对同一部分资源进行操作会产生竞争,所以此时需要同步来确保串行访问共享区。

Java中实现同步的方法

1.锁对象

ReentrantLock 公平策略锁

2.条件对象

线程运行到共享区,发现条件不满足,进入等待状态,放弃锁。

直到某个线程进入完成操作出共享区之前激活所有等待这个条件的线程。

一个锁对象可以有多个相关联的条件对象

3 . synchronized关键字

4.同步块

5.监视器

6 . volatile字段

7 . final变量

线程的生命周期

image.png

死锁

四个必要条件: 1.互斥条件:

2.请求与保持条件 : 不释放已获得的资源

3.不剥夺条件:不能强行剥夺资源

4.循环等待: 互相等待对方释放资源

线程的调度

高优先级可以抢占资源

同优先级随机被选择

线程安全

Java中线程安全的API的安全是相对的,不是绝对的

互斥同步也称阻塞同步: 悲观并发策略

  • 临界区、互斥量、信号量
  • Synchronized
  • 重入锁 ReentranLock 也称公平策略锁
  • Synchronized表现在语法层面,ReentranLock表现在API层面,当线程规模大ReentranLock的性能较好且平稳

非阻塞同步: 乐观并发策略

  • 使用硬件处理器指令进行不断重试策略

无同步:

  • ThreadLocal

锁优化:

  • 自旋锁:自旋(占有资源循环等待)Java默认为10次
  • 自适应锁:由上一次是否自旋是否成功决定是否延长等待时间
  • 锁消除:JVM即时编译器自动消除无用锁
  • 锁粗化:出现反复加锁和循环体内加锁,将同步范围扩大
  • 偏向锁:偏向第一个获得锁的线程

简单实例: 程序描述,实现一个线程安全的售票过程 类:Ticket 、 Consumer 、 Producer 、 ProducerAndConsumer(Application)

public class Ticket {
    
    /**
     * number 暂存票号
     * size 总票数
     * i 当前售票票号
     * available 标志 -- true 有票 false 无票
     */
    
    int number = 0;
    int size;
    int i = 0;
    boolean available = false;
    
    public Ticket(int size){
        this.size = size;
    }
    public synchronized void put(){
        //如果存在票代售,则存票线程等待
        if(available){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Producer puts ticket " + (++number));
        available = true;
        notify(); //存票后唤醒售票线程
    }
    
    public synchronized void sell(){
        //如果没有存票,则售票线程等待
        if(!available) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Consumer buys ticket " + (++i));
        available = false;
        notify(); //售票结束后唤醒存票线程
        if(number == size){
            number = size + 1;
        }
    }
    
}
public class Consumer extends Thread{
    Ticket t = null;
    int i = 0;
    
    public Consumer(Ticket t){
        this.t = t;
    }
    
    @Override
    public void run(){
        while(t.number < t.size){
                t.sell();
        }
    }
}
public class Producer extends Thread{
    Ticket t = null;
    
    public Producer(Ticket t){
        this.t = t;
    }
    
    @Override
    public void run(){
        while(t.number < t.size){
            t.put();
        }
    }
    
}
public class ProducerAndConsumer {
    public static void main(String[] args) {
        Ticket ticket = new Ticket(10);
        new Consumer(ticket).start();
        new Producer(ticket).start();
    }
}