多线程

多线程

多线程实现方式

  1. 继承Thread

    public class MyThread extends Thread{
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " - - - " + i);
            }
        }
    }
    
  2. 实现runnable

    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " - - - " + i);
            }
        }
    }
    
  3. 实现callable

    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " - - - " + i);
            }
            return Thread.currentThread().getName() + " - - - 执行完毕!";
        }
    }
    
  4. 设置和获取线程名称

    Thread t = new Thread();
    
    t.setName('线程名称');
        
    Thread.currentThread().getName();
    
  5. Thread中的方法

    // 休眠1秒
    Thread.sleep(1000);
    
  6. 线程优先级

    Thread t = new Thread();
    
    // min 5 -> max 10
    t.setPricrity();
    
  7. 守护线程

    MyThread1 t1 = new MyThread1();
    
    MyThread2 t2 = new MyThread2();
    
    t1.setName("nvshen");
    t2.setName("beitai");
    
    // 设置t2为守护线程
    // 当t1执行完毕,t2也没有继续运行的必要了
    t2.setDaemon(true);
    
    t1.start();
    t2.start();
    

线程安全问题

  1. 卖票案例

    class MyRunable implements Runnable{
        private Integer tickets = 100;
        @Override
        public void run() {
            while (true){
                if (tickets <= 0){
                    break;
                }else {
                    tickets--;
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖出第" + (tickets + 1) + "张票,剩余" + tickets + "张");
                }
            }
        }
    }
    
    
    public static void main(String[] args) {
            MyRunable myRunable = new MyRunable();
            Thread t1 = new Thread(myRunable);
            t1.setName("【窗口 1】");
            Thread t2 = new Thread(myRunable);
            t2.setName("【窗口 2】");
            Thread t3 = new Thread(myRunable);
            t3.setName("【窗口 3】");
            t1.start();
            t2.start();
            t3.start();
    }
    
     ......
    【窗口 2】:在卖第11张票,剩余10张
    【窗口 3】:在卖第8张票,剩余7张
    【窗口 1】:在卖第8张票,剩余7张
    【窗口 2】:在卖第8张票,剩余7张
    【窗口 3】:在卖第5张票,剩余4张
    【窗口 1】:在卖第5张票,剩余4张
    【窗口 2】:在卖第5张票,剩余4张
    【窗口 2】:在卖第2张票,剩余1张
    【窗口 1】:在卖第2张票,剩余1张
    【窗口 3】:在卖第2张票,剩余1张
    【窗口 2】:在卖第1张票,剩余0张
    
    
    【问题分析】:
    	重复票:
    	tickets = 100
    	线程一执行tickets--后,tickets变为99,此时执行权被线程二抢走,线程二执行完tickets--后,tickets变为98,此时线程一中的tickets值也是98,就出现了重复卖票问题
    	
    	负号票:
    	tickets = 1
        线程一执行tickets--之后,执行权被线程二抢走,此时tickets数量变为0,线程二又执行tickets--之后,此时tickets变为-1,执行权又被线程三抢走,此时就出现符负号票问题
    
  2. 同步代码块 | 同步方法

    synchronized(锁对象){
        
    }
    
    锁对象要唯一,多个线程要使用同一把锁
    
    private synchronized void synchronizedMethod(){
    
    }
    
  3. Lock锁

    private ReentrantLock lock = new ReentrantLock();
    
    // 加锁
    lock.lock();
        
    // 解锁
    lock.unlock();
    
  4. 死锁

    public class deadLock {
        public static void main(String[] args) {
            Object o1 = new Object();
            Object o2 = new Object();
    
            new Thread(()->{
                while (true){
                    synchronized (o1){
                        synchronized (o2){
                            System.out.println("张三在吃饭");
                        }
                    }
                }
            }).start();
    
            new Thread(()->{
                while (true){
                    synchronized (o2){
                        synchronized (o1){
                            System.out.println("李四在洗脸");
                        }
                    }
                }
            }).start();
        }
    }
    
    【第一个线程o1上锁后执行权被第二个线程抢走,o2上锁,此时线程一中拿不到o2锁,线程二中拿不到o1锁,产生死锁】
    

线程池

虚拟机中线程六种状态:
新建(NEW)创建线程对象
就绪(RUNNABLE)start方法
阻塞(BLOCKED)无法获得锁对象
等待(WAITING)wait方法
计时(TIMED_WAITING)sleep方法
结束(TERMINATED)全部代码运行完毕
// 创建默认线程池
public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName());
        });
    
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName());
        });

        executorService.shutdown();
}
// 创建指定最多线程数的线程池
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();
}
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());
        });

    	// 获取线程池内线程数量
    	System.out.println(executorService.getPoolSize())
    	
        executorService.shutdown();
}

线程池对象-ThreadPoolExecutor

// 源码
public ThreadPoolExecutor(int corePoolSize,	
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}
// corePoolSize							核心线程数
// maximumPoolSize						最大线程数	
// keepAliveTime						空闲线程存活时间
// unit									单位(TimeUnit.xx)
// workQueue							任务队列(阻塞队列)	new ArrayBlockingQueue(10)
// Executors.defaultThreadFactory()		线程工厂
// defaultHandler						任务拒绝策略		   new ThreadPoolExecutor().abortPolicy() 终止策略,超过的任务就不要了
参数解释示例
corePoolSize核心线程数5
maximumPoolSize最大线程数10
keepAliveTimeunit空闲线程存活时间10
unit单位TimeUnit.SECOND
workQueue任务队列(阻塞队列)new ArrayBlockingQueue(10)
Executors.defaultThreadFactory()线程工厂Executors.defaultThreadFactory()
defaultHandler任务拒绝策略new ThreadPoolExecutor().abortPolicy终止策略,超过的任务就不要了
任务拒绝策略
ThreadPoolExecutor().abortPolicy默认策略,丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy丢弃任务,不抛出异常 不推荐的做法
ThreadPoolExecutor.DiscardOldestPolicy抛弃等待最久的任务,然后把当前任务加入队列
ThreadPoolExecutor.CallerRunsPolicy调用任务的run()方法绕过线程池直接执行

volatile

// volatile修饰共享数据
public static volatile int money = 100000;

原子性

	所谓原子性是指在一次操作中,要么所有操作全部都得到执行且不会受任何因素的干扰而中断,要么所有操作都不执行,`多个操作是一个不可以分割的整体`
public class 原子性 {
    public static void main(String[] args) {
        MyAtomThread ma = new MyAtomThread();
        for (int i = 0; i < 100; i++) {
            new Thread(ma).start();
        }
    }
}

class MyAtomThread implements Runnable{
    private int count = 0;

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            count++;
            System.out.println("已经送了" + count + "个冰淇淋");
        }
    }
}
已经送了9988个冰淇淋
已经送了9989个冰淇淋
已经送了9990个冰淇淋
已经送了9991个冰淇淋
已经送了9992个冰淇淋
已经送了9993个冰淇淋
已经送了9994个冰淇淋
已经送了9995个冰淇淋
已经送了9996个冰淇淋
已经送了9997个冰淇淋
已经送了9998个冰淇淋
已经送了9999个冰淇淋

// volatile不能保证原子性

// 同步代码块方式

// jdk1.5 出现atomic原子包
// AtomicInteger

public class 原子性 {
    public static void main(String[] args) {
        MyAtomThread ma = new MyAtomThread();
        for (int i = 0; i < 100; i++) {
            new Thread(ma).start();
        }
    }
}

class MyAtomThread implements Runnable{
    AtomicInteger count = new AtomicInteger(0);

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("已经送了" + count.addAndGet(1) + "个冰淇淋");
        }
    }
}
AtomicInteger中的方法解释
int get()获取值
int getAndIncrement()自增1,返回自增前的值
int IncrementAndGet()自增1,返回自增后的值
int addAndGet(x)增加x,返回增加后的值
int getAndSet(x)设置为x,返回旧值

AtomicInteger原理: 自旋锁 + CAS算法

# CAS算法:有3个操作数(内存值V,旧的预期值A,要修改的值B)
	当旧的预期值A == 内存值,此时修改成功,将V改为B
	当旧的预期值A ! = 内存值,此时修改失败,不做任何操作
	并从新获取现在的最新值(这个重新获取的动作就是自旋)

synchronized和CAS区别

# 相同点:
 	synchronized每次操作共享数据都会上锁。(悲观锁)

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

并发工具类

1. HashMap 是线程不安全的,多线程环境下会有数据安全问题;
2. Hashtable 是线程安全的,但是会将整张表锁起来,效率低下;
3. ConcurrentHashMap 也是线程安全的,效率较高。

jdk1.7前ConcurrentHashMap原理解析:
创建对象:
	  创建一个默认长度为16,默认加载因子为0.75的数组,数组名为Segment
	  再创建一个长度为2的小数组,把地址值赋值给0索引,其他索引都是null
添加:
	  第一次会根据键的哈希值来计算出在大数组中应存入的位置,如果为null,则按照模板创建小数组,创建完毕,会二次哈希,计算出在小数组中应存入的位置,为null直接存入,如果不为null,就会根据记录的地址值找到小数组,二次哈希,计算出在小数组中应存入的位置,如果需要扩容,则将小数组扩容两倍,如果不需要扩容,则就会看小数组的这个位置有没有元素,如果没有元素,则直接存,如果有元素,旧会调用equals方法,比较属性值,如果equals为true,则不存,如果是false,形成哈希桶结构。

jdk1.8后ConcurrentHashMap原理解析:
	底层结构:哈希表(数组、链表、红黑树的结合体)
	结合CAS机制 + synchronized同步代码块形式保证线程安全
	
	如果使用空参构造创建ConcurrentHashMap对象,则什么事情都不做,在第一次添加元素的时候创建哈希表;
	计算当前元素应存入的索引;
	如果该索引位置为null,则利用cas算法,将本届点添加到数组中;
	如果该索引位置不为null,则利用volayile关键字获得当前位置最新的结点地址,挂在他下面,变成链表;
	当链表的长度大于等于8,自动转换成红黑树;
	以链表或者红黑树头结点为锁对象,配合悲观锁保证多线程操作集合时数据的安全性。

CountDownLatch

public class CountDownLatch_demo {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        new Thread(new Mother(countDownLatch)).start();
        new Thread(new Son1(countDownLatch)).start();
        new Thread(new Son2(countDownLatch)).start();
        new Thread(new Son3(countDownLatch)).start();
    }
}


class Mother implements Runnable{
    private CountDownLatch countDownLatch;
    public Mother(CountDownLatch countDownLatch){
        this.countDownLatch = countDownLatch;
    }
    
    @Override
    public void run() {
        try {
            // 妈妈线程在等待
            countDownLatch.await();
            System.out.println("妈妈收拾碗筷!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Son1 implements Runnable{
    private CountDownLatch countDownLatch;
    public Son1(CountDownLatch countDownLatch){
        this.countDownLatch = countDownLatch;
    }
    
    @Override
    public void run() {
        System.out.println("son1吃完了");
        countDownLatch.countDown();
    }
}

class Son2 implements Runnable{
    private CountDownLatch countDownLatch;
    public Son2(CountDownLatch countDownLatch){
        this.countDownLatch = countDownLatch;
    }
    
    @Override
    public void run() {
        System.out.println("son2吃完了");
        countDownLatch.countDown();
    }
}

class Son3 implements Runnable{
    private CountDownLatch countDownLatch;
    public Son3(CountDownLatch countDownLatch){
        this.countDownLatch = countDownLatch;
    }
    
    @Override
    public void run() {
        System.out.println("son3吃完了");
        countDownLatch.countDown();
    }
}

# 使用场景
让某一条线程等待其他线程执行完毕之后再执行。
方法解释
public CountDownLatch(int count)参数传递线程数,表示等待线程数量,并等以一个计数器
public void await()让线程等待,计数器为0,唤醒等待线程
public void countDown()当前线程执行完毕,计数器-1

Semaphore

public class Semaphore_demo {
    public static void main(String[] args) {
        MyRunnable_Semaphore myRunnable_semaphore = new MyRunnable_Semaphore();
        for (int i = 0; i < 10; i++) {
            new Thread(myRunnable_semaphore).start();
        }
    }
}

class MyRunnable_Semaphore implements Runnable{
    Semaphore semaphore = new Semaphore(2);

    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + "获得通行证");
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName() + "归还通行证");
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
Thread-2获得通行证
Thread-6获得通行证
Thread-2归还通行证
Thread-6归还通行证
Thread-4获得通行证
Thread-9获得通行证
Thread-4归还通行证
Thread-9归还通行证
Thread-5获得通行证
Thread-8获得通行证
Thread-8归还通行证
Thread-5归还通行证
Thread-1获得通行证
Thread-7获得通行证
Thread-1归还通行证
Thread-7归还通行证
Thread-3获得通行证
Thread-0获得通行证
Thread-3归还通行证
Thread-0归还通行证
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

obud

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值