多线程详解

多线程详解

  • 创建线程方式1:继承Thread类
  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class TestThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("我在看代码"+i);
        }
    }
    public static void main(String[] args) {
        TestThread testThread01 = new TestThread();
        //start方法拉起一个线程,两个程序交错执行,cpu安排调度
        testThread01.start();
        //run方法顺序执行
        //testThread01.run();
        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习多线程----" + i);
        }
    }
}
  • 创建线程方式2:实现Runnable接口
  1. 自定义线程类MyRunnable类实现Runnable接口
  2. 重写run()方法,编写线程执行体
  3. 创建Thread对象,创建MyRunnable对象作为Thread对象的入参,调用start()方法启动线程
public class TestThread02 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("我在看runnable代码"+i);
        }
    }
    public static void main(String[] args) {
        TestThread testThread01 = new TestThread();
        //start方法拉起一个线程,两个程序交错执行,cpu自动调度
        new Thread(testThread01).start();
        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习runnable多线程----" + i);
        }
    }
}
  • 多个线程操作同一个对象(买火车票)

  • callable实现要重写call方法,有返回值

静态代理

  1. 静态代理模式总结:
  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要代理真实角色
  1. 好处:
  • 代理对象可以做很多真实对象做不了的事情
  • 真实对象专注做自己的事情

案例:

public class StaticProxy {
    public static void main(String[] args) {
        You you = new You();
//        new Thread( ()-> System.out.println("我爱你") ).start();

        WeddingCompany weddingCompany = new WeddingCompany(you);
        weddingCompany.HappyMarry();
    }
}
interface Marry{
    void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("秦老师要结婚了!");
    }
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
    private Marry target;
    public WeddingCompany (Marry target){
        this.target = target;
    }
    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }
    public void before(){
        System.out.println("布置婚礼现场");
    }
    public void after(){
        System.out.println("支付尾款");
    }
}

Lambda表达式

  1. 函数式接口(Functional Interface):
  • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
  • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。
  • 有了函数式接口才能使用lambda表达式
  1. lambda简化:
  • 简化过程:外部类-->静态内部类-->局部内部类-->匿名内部类-->lambda表达式
  • (参数列表)->{//参数列表可以继续省掉类型声明,一个形参可以省略括号,方法体只有一句可以省略

参考lambda表达式及简化

/*
推导lambda表达式
 */

public class TestLambda1 {

    //3.静态内部类
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("i like lambda2");
        }
    }
    public static void main(String[] args) {
        ILike iLike = new Like();
        iLike.lambda();
        iLike = new Like2();
        iLike.lambda();

        //4.局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }
        iLike = new Like3();
        iLike.lambda();

        //5.匿名内部类
        iLike = new ILike(){
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        iLike.lambda();

        //6.用lambda简化
        iLike = () -> {
            System.out.println("i like lambda5");//单行输出
        };
        iLike.lambda();

        //
    }
}
//1.定义一个函数式接口
interface ILike{
    void lambda();
}
//2.实现类
class Like implements ILike{

    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

输出:
i like lambda
i like lambda2
i like lambda3
i like lambda4
i like lambda5

守护线程

  • 线程分为用户线程守护线程(daemon)
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如,后台记录操作日志,监控内存,垃圾回收等待
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认false表示用户线程,正常的线程都是用户线程
        thread.start();

        new Thread(you).start();

    }
}

class God implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("上帝保佑者你。。。");
        }
    }
}

class You implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你一生都开心的活着。。。第" + i + "天");
        }
        System.out.println("goodbye,the world...");
    }
}
  • 不安全集合ArrayList<>()通过加锁实现安全
import java.util.ArrayList;
//synchronized(obj){}  默认给当前对象this上锁
public class UnSafeList {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            //手动给list加锁
            synchronized (list){
                new Thread(()->{
                    list.add(Thread.currentThread().getName());
                }).start();
            }
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
  • 安全集合CopyOnWriteArrayList<>() 安全的,通过volatile关键字保证唯一,transient保证序列化的
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合CopyOnWriteArrayList
public class TestJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

Lock(锁)

  • 从JDK5.0开始,Java提供了更强大的线程同步机制--通过显示定义同步锁对象来实现同步。同步锁使用Lock对象充当

  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象

  • ReentrantLock(可重入锁),它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可显式加锁、释放锁。

//测试Lock锁
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}
class TestLock2 implements Runnable {
    int ticketNums = 10;
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();//加锁
                if (ticketNums > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                } else {
                    break;
                }
            } finally {
                lock.unlock();//解锁
            }
        }
    }
}
  • synchronized和Lock的对比:
  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭),synchronized是隐式锁,出了作用域自动释放
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁
  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好地扩展性(提供更多的子类)
  4. 优先使用顺序:Lock >同步代码块(已经进入方法体,分配了相应资源)>同步方法(在方法体之外)

生产者消费者模式(一个问题)

package com.yunni.thread;/**
 * @author niyun
 * @date 2021/3/18 19:38
 */

import java.time.Duration;
import java.time.LocalTime;
//测试:生产者消费者模型
//生产者 , 消费者 , 产品 , 缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}
//生产者
class Productor extends Thread{
    SynContainer container;//成员变量
    public Productor(SynContainer container){
        this.container = container;
    }
    //生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了"+i+"只鸡");
        }
    }
}
class Consumer extends Thread{
    SynContainer container;//成员变量
    public Consumer(SynContainer container){
        this.container = container;
    }
    //消费
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Chicken ch = container.pop();
            System.out.println("消费了第"+ch.id+"只鸡");
        }
    }

}
//产品
class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}
class SynContainer{
    //需要一个容器
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {
        //如果容器满了,就需要等待消费者消费
        if (count==chickens.length){
            //通知消费者消费,成产等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有满,我们就需要丢入产品
        chickens[count]=chicken;
        count++;


        //可以通知消费者消费了
        this.notifyAll();
    }
    public synchronized Chicken pop() {
        //如果容器空了,就需要等待生产者生产
        if (count==0){
            //通知生产者生产,消费等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        Chicken chicken = chickens[count];


        //可以通知生产者生产了
        this.notifyAll();
        return chicken;
    }
}

posted @ 2021-03-22 22:39  血^_^月  阅读(40)  评论(0)    收藏  举报