Java学习笔记day23-多线程高级

多线程高级

Java学习笔记day23-多线程高级_第1张图片

虚拟机中线程的六种状态

  • 新建状态(NEW)-------------创建线程对象
  • 就绪状态(RUNNABLE)-----------------start方法
  • 阻塞状态(BLOCKED)----------------无法获得锁对象
  • 等待状态(WAITING)---------------wait方法
  • 计时等待(TIMED_WAITING)---------------sleep方法
  • 结束状态(TERMINATED)----------------全部代码运行完毕
线程池
代码实现
  • 创建一个池子,池子中是空的-----------------创建Executors中的静态方法

  • 有任务需要执行时创建线程对象;任务执行完毕,线程对象归还给池子----------------submit方法

    (池子会自动帮我们创建对象,任务执行完毕,也会自动把线程对象归还给池子)

  • 所有的任务执行完毕,关闭连接池-----------------shutdown方法

创建默认线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        //创建一个池子,池子中是空的,默认可以容纳int类型的最大值
        ExecutorService executorService = Executors.newCachedThreadPool();
        //Executors----可以帮我们创建线程池对象
        //ExecutorService----可以帮助我们控制线程池
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        //Thread.sleep(2000);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        executorService.shutdown();
    }
}

创建指定上限的线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolTest2 {
    public static void main(String[] args) {
        //参数不是初始值而是最大值
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        executorService.shutdown();
    }
}
手动创建线程池
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolTest3 {
    public static void main(String[] args) {
        //参数一:核心线程数量----不能小于0
        //参数二:最大线程数----不能小于等于0,最大数量>=核心线程数量
        //参数三:空闲线程最大存活时间----不能小于0
        //参数四:时间单位----TimeUnit
        //参数五:任务队列----不能为null,让任务在队列中等着,等有线程空闲了,在从队列中获取任务并执行
        //参数六:创建线程工厂----不能为null,按照默认的方式创建线程对象
        //参数七:任务的拒绝策略----不能为null,当提交的任务>池子中最大的线程数量+队列容量时拒绝任务;
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        pool.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        pool.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
        pool.shutdown();
    }
}
任务拒绝策略

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;默认策略

ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常,这是不推荐的做法;

ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中;

ThreadPoolExecutor.CallerRunsPolicy:调用任务的run()方法绕过线程池直接执行

Volatile
  1. 堆内存是唯一的,每一个线程都有自己的线程栈;
  2. 每一个线程在使用堆里面变量的时候,都会先拷贝一份到到变量的副本中;
  3. 在线程中,每一次使用是从变量的副本中获取的;

问题:

如果A线程修改了堆中共享的变量,那么其他线程不一定能及时使用最新的值。

Volatile关键字,强制线程每次在使用的时候,都会看一下共享区域最新的值。

示例:

public class LittleFish {
    public static volatile int fish = 5;
}

public class WhiteCat extends Thread {
    @Override
    public void run() {
        while (LittleFish.fish == 5){

        }
        System.out.println("小鱼干不够5斤了");
    }
}

public class BlackCat extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            LittleFish.fish = 4;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        WhiteCat wc = new WhiteCat();
        wc.setName("小白");
        wc.start();
        BlackCat bc = new BlackCat();
        bc.setName("小黑");
        bc.start();
    }
}
原子性

​ 所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断;要么所有的操作都不执行,多个操作是一个不可分割的整体;

volatile关键字:只能保证线程每次在使用共享数据的时候是最新值,但是不能保证原子性。

原子类AtomicInteger
  • public AtomicInteger():初始化一个默认值为0的原子型Integer
  • public AtomicInteger(int initialValue):初始化一个指定值的原子型Integer
  • int get():获取值
  • int getAndIncrement():以原子方式将当前值加1,注意,这里返回的是自增前的值
  • int incrementAndGet():以原子方式将当前值加1,注意,这里返回的是自增后的值
  • int addAndGet(int data):以原子方式将输入的数值与实例中的值(AtomicInteger里的Value)相加,并返回结果
  • int getAndSet(int value):以原子方式设置为newValue的值,并返回旧值

示例:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {
    public static void main(String[] args) {
        AtomicInteger ai = new AtomicInteger();
        System.out.println(ai); //0
        AtomicInteger ai2 = new AtomicInteger(10);
        System.out.println(ai2); //10
        System.out.println(ai2.get()); //10
        int andIncrement = ai2.getAndIncrement();
        System.out.println(andIncrement); //10
        System.out.println(ai2.get()); //11
        int i = ai2.incrementAndGet();
        System.out.println(i); //12
        System.out.println(ai2.get()); //12
        int i1 = ai2.addAndGet(10);
        System.out.println(i1); //22
        System.out.println(ai2.get()); //22
        int andSet = ai2.getAndSet(10);
        System.out.println(andSet); //22
        System.out.println(ai2.get()); //10
    }
}
synchronized和CAS的区别

相同点:在多线程情况下,都可以保证共享数据的安全性。

不同点:synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每次操作共享数据之前,都会上锁。(悲观锁)

CAS是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查以下,别人有没有修改过这个数据。如果别人修改过,那么再次获取现在的最新值。如果别人没有修改过,那么直接修改共享数据的值。(乐观锁)

并发工具类
Hashtable

​ HashMap是线程不安全的(多线程环境下可能会存在问题);为了保证数据的安全性我们可以使用Hashtable,但是Hashtable效率低下;

  • Hashtable采取悲观锁synchronized的形式保证数据的安全性;
  • 只要有线程访问,会将整张表全部锁起来,所以Hashtable的效率低下;
ConcurrentHashMap

1.7原理

Java学习笔记day23-多线程高级_第2张图片

1.8原理

  1. 如果使用空参构造创建ConcurrentHashMap对象,则什么事情都不用做,在第一次添加元素的时候创建哈希表;
  2. 计算当前元素应存入的索引;
  3. 如果该索引位置为null,则利用CAS算法,将本节点添加到数组中;
  4. 如果该索引位置不为null,则利用volatile关键字获得当前位置最新的结点地址,挂在他下面,变成链表;
  5. 当链表的长度大于等于8时,自动转换成红黑树;
  6. 以链表或者红黑树头结点为锁对象,配合悲观锁保证多线程操作集合时数据的安全性;
CountDownLatch

使用场景:让某一条线程等待其他线程执行完毕后再执行。

方法 说明
public CountDownLatch(int count) 参数传递线程数,表示等待线程数量
public void await() 让线程等待
public void countDown() 当前线程执行完毕

示例:

import java.util.concurrent.CountDownLatch;

public class ChildThread1 extends Thread {
    CountDownLatch countDownLatch;
    public ChildThread1(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        //吃饺子
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "在吃第" + i + "个饺子");
        }
        //吃完说一声
        //每一次调用countDown方法的时候,就让计数器-1
        countDownLatch.countDown();
    }
}

public class ChildThread2 extends Thread {
    CountDownLatch countDownLatch;
    public ChildThread2(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        //吃饺子
        for (int i = 0; i < 15; i++) {
            System.out.println(getName() + "在吃第" + i + "个饺子");
        }
        //吃完说一声
        //每一次调用countDown方法的时候,就让计数器-1
        countDownLatch.countDown();
    }
}

import java.util.concurrent.CountDownLatch;

public class ChildThread3 extends Thread {
    CountDownLatch countDownLatch;
    public ChildThread3(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        //吃饺子
        for (int i = 0; i < 12; i++) {
            System.out.println(getName() + "在吃第" + i + "个饺子");
        }
        //吃完说一声
        //每一次调用countDown方法的时候,就让计数器-1
        countDownLatch.countDown();
    }
}
import java.util.concurrent.CountDownLatch;

public class MotherThread extends Thread {
    CountDownLatch countDownLatch;
    public MotherThread(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        //等待
        try {
            //当计数器变成0的时候,会自动唤醒这里等待的线程
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //收拾碗筷
        System.out.println("妈妈在收拾碗筷");
    }
}

import java.util.concurrent.CountDownLatch;

public class MyCountDownLatchTest {
    public static void main(String[] args) {
        //创建CountDownLatch的对象,需要传递给四个线程
        //在底层定义了一个计数器,此时计数器的值就是3
        CountDownLatch countDownLatch = new CountDownLatch(3);
        //创建4个线程对象并开启他们
        MotherThread mt = new MotherThread(countDownLatch);
        mt.start();
        ChildThread1 ct1 = new ChildThread1(countDownLatch);
        ChildThread2 ct2 = new ChildThread2(countDownLatch);
        ChildThread3 ct3 = new ChildThread3(countDownLatch);
        ct1.setName("小红");
        ct2.setName("小明");
        ct3.setName("小兰");
        ct1.start();
        ct2.start();
        ct3.start();

    }
}
Semaphore

使用场景:可以控制访问特定资源的线程数量。

Java学习笔记day23-多线程高级_第3张图片

  1. 需要有人管理这个通道----------------创建Semaphore对象

  2. 当有车进来了,发通行许可证-----------------acquire()发通行证

  3. 当车出去了,收回通行许可证------------------release()收回通行证

  4. 如果通行许可证发完了,那么其他车辆只能等着

代码实现:

import java.util.concurrent.Semaphore;

public class MyRunnable implements Runnable {
    //获得管理员对象
    private Semaphore semaphore = new Semaphore(2);
    @Override
    public void run() {
        //获得通行证
        try {
            semaphore.acquire();
            //开始行使
            System.out.println("获得了通行证开始行驶");
            Thread.sleep(2000);
            System.out.println("归还通行证");
            //归还通行证
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class MySemaphoreTest {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
        for (int i = 0; i < 50; i++) {
            new Thread(mr).start();
        }
    }
}

你可能感兴趣的:(笔记,java,学习,开发语言)