Java多线程

目录

线程的状态

新建(New)、可运行(Runnable)、阻塞(Blocking)、无限期等待(Waiting)、有限期等待(Timed-Waiting)、死亡(Terminated)

使用线程

  • 实现Callable接口
  • 实现Runnable接口
  • 继承Thread类

线程机制

用户线程与守护(Daemon)线程

守护线程时提供通用服务的线程(例如垃圾回收线程),优先级很低
通过Thread对象的setDaemon(boolean)方法设置守护线程与用户线程,注意此方法必须在线程对象的start方法之前调用,否则会抛出java.lang.IllegalThreadStateException异常
守护线程创建的线程默认是守护线程,用户线程同理。
守护线程是为了服务用户线程而存在的,所有用户线程结束之后,jvm就退出了,守护线程自然也结束,所以守护线程的结束时间不确定

睡眠

线程的sleep方法(静态方法),休眠当前线程,当前线程进入timed-waiting状态

yield

线程对象的yield方法表明该线程已经完成了最重要的工作,尝试让出cpu占用,把cpu使用权让给同优先级的其他线程

中断

interrupt()

通过调用线程对象的interrput方法中断该线程,如果该线程处于等待/阻塞状态,会抛出java.lang.InterruptedException异常。
该方法不能中断处于I/O阻塞或synchronized阻塞的线程
线程对象的该方法被调用后,会设置阻断标志为true,通过interrupted()方法检查是否有人调用了本线程的interrupt方法

interrupted()

检查是否有人调用了本线程的interrupt方法

中断的应用

线程池的shutdownNow方法就是通过调用所有工作线程的interrupt方法来中断工作线程

线程池

https://www.cnblogs.com/darknessplus/p/10359256.html

线程安全的实现方法

不可变

互斥同步

synchronized关键字与ReentrantLock

都是可重入锁
synchronized关键字由jvm实现,ReentrantLock由jdk实现
ReentrantLock提供比synchronized更高级的功能

  • 等待可中断
  • 公平锁
  • 选择性通知

非阻塞同步

无同步

线程本地存储

ThreadLocal类
ThreadLocal 不是用来协调多线程的,而是为同一个线程的不同方法提供共享变量
一个线程的ThreadLocal变量在任何方法内都是可见的
线程Thread保存了ThreadLocalMap,ThreadLocalMap保存了本线程的所有ThreadLocal数据
ThreadLocal提供的方法

public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }

线程之间的协调

多线程协作解决问题时,需要对他们的执行顺序进行协调

join

调用另一个线程对象的join方法表示本线程等待另一个线程执行完成后再继续执行

wait、notify、notifyAll

wait、notify是Object类提供的final方法,不可重写。
通过调用某个对象的wait方法,释放本线程对该对象持有的锁,进入阻塞状态。
另一个线程调用该对象的notify方法,唤醒该对象的锁的阻塞队列中的一个线程,notifyAll方法唤醒该对象的等待队列的所有线程。
wait期间,线程会释放锁,不然会死锁。
配合synchronized使用,再同步代码中使用。

wait与sleep的区别:

  • wait是Object方法,sleep是Thread的静态方法
  • wait会释放锁,sleep不会

生产者消费者问题:

public class Wait {

    public static void main(String[] args) throws InterruptedException {
        LinkedList<Integer> list = new LinkedList<Integer>();
        Factory factory = new Factory();
        factory.setList(list);
        factory.setMaxSize(10);
        for(int i=1;i<=10;i++)
        {
            Producer producer = new Producer();
            producer.setNum(i);
            producer.setFactory(factory);
            producer.start();

            Consumer consumer = new Consumer();
            consumer.setNum(i);
            consumer.setFactory(factory);
            consumer.start();
        }
    }
}
class Producer extends Thread{
    private AbstractFactory factory;
    private int num;

    public void setNum(int num) {
        this.num = num;
    }

    public void setFactory(AbstractFactory factory) {
        this.factory = factory;
    }

    @Override
    public void run() {
        factory.produce(num);
    }
}
class Consumer extends Thread{
    private AbstractFactory factory;
    private int num;

    public void setFactory(AbstractFactory factory) {
        this.factory = factory;
    }

    public void setNum(int num) {
        this.num = num;
    }

    @Override
    public void run() {
       factory.consume(num);
    }
}
class Factory implements AbstractFactory{
    private List<Integer> list;
    private int maxSize;

    public void setList(List<Integer> list) {
        this.list = list;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    public void produce(int num) {
        synchronized (list)
        {
            while(num+list.size()>maxSize)
            {
                try {
                    System.out.println("仓库已满。要生产的"+num+",库存容量"+maxSize);
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int size=list.size();
            for(int i=size;i<size+num;i++)
            {
                list.add(i);
            }
            System.out.println("生产"+num+"件,总共"+list.size()+"件");
            list.notifyAll();
        }
    }

    public void consume(int num) {
        synchronized (list)
        {
            while(list.size()-num<0)
            {
                try {
                    System.out.println("仓库已空。要消费的"+num+",库存容量"+list.size());
                    list.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            int size=list.size();
            for(int i=size-1;i>=size-num;i--)
            {
                list.remove(i);
            }
            System.out.println("消耗"+num+"件,总共"+list.size()+"件");
            list.notifyAll();
        }
    }
}
interface AbstractFactory
{
    void produce(int num);
    void consume(int num);
}

await、signal、signalAll

对应着Object的wait和notify方法,Lock也有类似的机制。
通过Lock.newCondition()方法获取一个condition对象,通过condition对象的await和signal方法进行线程同步。
在使用condition对象的await和signal方法之前必须获得重入锁,调用signal方法后最好释放重入锁。

JUC包

AQS

CountdownLatch

控制一个线程等待其他线程

public static void main(String[] args) throws InterruptedException {
        final CountDownLatch count = new CountDownLatch(10);
        for(int i=0;i<10;i++){
            new Thread(){
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    count.countDown();
                }
            }.start();
        }
        count.await();
        System.out.println("END");
    }

输出:

Thread-0
Thread-2
Thread-3
Thread-1
Thread-5
Thread-4
Thread-6
Thread-7
Thread-8
Thread-9
END

Semaphore

信号量,控制并发线程数

public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(1);
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    try {
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "正在运行");
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        System.out.println(Thread.currentThread().getName() + "结束");
                        semaphore.release();
                    }
                }
            }.start();
        }
    }

输出:

Thread-5正在运行
Thread-5结束
Thread-0正在运行
Thread-0结束
Thread-3正在运行
Thread-3结束
Thread-8正在运行
Thread-8结束
Thread-6正在运行
Thread-6结束
Thread-2正在运行
Thread-2结束
Thread-7正在运行
Thread-7结束
Thread-1正在运行
Thread-1结束
Thread-4正在运行
Thread-4结束
Thread-9正在运行
Thread-9结束

其他组件

FutureTask

获得任务的返回值

BlockingQueue

阻塞队列,在线程池里有应用

乐观锁和悲观锁

悲观锁

对数据更新的冲突持保守态度,认为总会发生冲突。策略是在处理数据时加锁

乐观锁

乐观锁认为发生冲突的情况比较少,不加锁,而是在更新数据的时候检查是否发生了冲突。

乐观锁的两种实现

版本号机制

为数据库表增加版本号字段,每次更新数据版本号+1。在修改数据前获取版本号,提交修改时检查两次版本号是否一致,如不一致说明数据更新发生了冲突。

CAS

Compare And Swap
现值V、旧值A、新值B,当且仅当V==A,更新B到数据库
硬件实现的原子操作

乐观锁的缺点

ABA问题

CAS,V==A并不能说明V没有发生过改变

自旋锁开销大

CAS与synchronized使用场景

CAS适用读多写少,synchronized适用写多(冲突多)

synchronized和ReenTrantLock的区别

  • 都是可重入
  • synchronized由jvm实现,ReenTrantLock时jdk api
  • ReenTrantLock高级功能:中断等待、公平锁、选择性通知
  • 两者性能相差无几
    原文作者:darknessplus
    原文地址: https://www.cnblogs.com/darknessplus/p/10357698.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞