Java多线程

本文详细解析了程序、进程、线程的基本概念,探讨了多线程实现的两种方式,包括继承Thread和实现Runnable接口,以及Callable接口的使用。随后介绍了线程池、静态代理和Lambda表达式的应用。涵盖了线程状态、控制方法如stop、sleep和yield,以及线程优先级和守护线程。文章还讨论了死锁、生产者消费者问题和同步解决方案,如管程法和信号灯法。

基本概念

  • 程序

    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念

  • 进程

    进程是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位

  • 线程

    通常在一个进程中可以包含若干个线程,一个进程中至少有一个线程。线程是CPU调度和执行的单位


多线程实现

继承Thread类 extends Thread

  • 继承Thread类,重写run()方法,调用start开启线程
  • 注意:线程开启不一定立即执行,由CPU调度执行
  • 不建议使用:OOP单继承局限性
public class TestThread01 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 100; i++) {
            System.out.println("run--"+i);
        }
    }

    public static void main(String[] args) {
        //mian线程,主线程

        //创建一个线程对象
        TestThread01 testThread01 = new TestThread01();
        //调用start方法开启线程
        testThread01.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main--"+i);
        }
    }
}

实现runnable接口 implements Runnable

  • 创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
  • 推荐使用:避免单继承局限性,灵活方便,同一个对象能被多个线程使用
public class TestThread03 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("run---"+i);
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread03 testThread03 = new TestThread03();
        //创建线程对象,通过线程对象来开启线程,代理
        new Thread(testThread03).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main---"+i);
        }
    }
}

实现callable接口 implements Callable<>

  • 可以自定义返回值
  • 可以抛出异常
public class TestCallable implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("-->"+i);
        }
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable testCallable = new TestCallable();
        //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> r1=ser.submit(testCallable);
        //获取结果
        boolean rs = r1.get();
        //关闭服务
        ser.shutdown();
    }
}

线程池

public class TestPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);//参数为池子大小

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();
    }
}

class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 2; i++) {
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

静态代理

  • 真实对象和代理对象要实现同一个接口
  • 代理对象要代理真实角色
  • 有了代理对象,很多事就不用真实对象自己做
public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.Marry();
    }
}

interface Marry{
    void Marry();
}
//真实角色,主人公
class You implements Marry{
    @Override
    public void Marry() {
        System.out.println("结婚");
    }
}
//代理角色,帮助作用
class WeddingCompany implements Marry{
    //代理真实目标角色
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void Marry() {
        before();
        this.target.Marry();
        after();
    }

    public void before(){
        System.out.println("结婚前,布置现场");
    }
    public void after(){
        System.out.println("结婚后,收尾款");
    }
}

Lamda表达式

  • jdk8新特性

  • 为什么要使用lamda表达式

    • 避免匿名内部类定义过多
    • 让代码看起来更简洁
    • 去掉了一堆无意义的代码,只留下核心逻辑

推导lamda表达式过程

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

        //4.局部内部类
        class Like3 implements ILike{
            @Override
            public void lamda() {
                System.out.println("I like lamda3");
            }
        }

        like=new Like3();
        like.lamda();

        //5.匿名内部类,没有类的名称,必须借助接口或者父类
        like=new ILike() {
            @Override
            public void lamda() {
                System.out.println("I like lamda4");
            }
        };
        like.lamda();
        //6.用lamda简化
        like=()->{System.out.println("I like lamda5");};
        like.lamda();
    }
}

//1.定义一个函数式接口
interface ILike{void lamda(); }

//2.实现类
class Like implements ILike{
    @Override
    public void lamda() {
        System.out.println("I like lamda1");
    }
}

更加简化

public class TestLamda02 {
    public static void main(String[] args) {
        ILove love=(int a)->{
            System.out.println("I love you"+a);
        };

        //简化1 去掉参数类型
        love=(a)->{
            System.out.println("I love you"+a);
        };

        //简化2 去掉括号
        //注意:若有多个参数,不能去掉括号
        love=a->{
            System.out.println("I love you"+a);
        };

        //简化3 去掉花括号
        //注意:lamda表达式只能有一行代码的情况下才能简化为一行,如果有多行代码,那么应用代码块包裹
        love=a->System.out.println("I love you"+a);

        love.love(520);
    }
}

interface ILove{ void love(int a);}

函数式接口 Functional Interface

  • 定义
  1. 任何接口,如果只包含唯一一个抽象方法,那它就是一个函数式接口

    public interface exam{
        public abstract void run();
    }
    
  2. 对于函数式接口,我们可以通过lamda表达式来创建该接口的对象

线程 thread

线程状态 state

  1. 新生状态:Thread t=new Thread() 线程对象一旦创建就进入到了新生状态
  2. 就绪状态:当调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行
  3. 运行状态:进入运行状态,线程菜真正执行线程体的代码块
  4. 阻塞状态:当调用sleep,wait或同步锁定时,线程进入阻塞状态,代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待CPU调度执行
  5. 死亡状态:线程中断或结束,一旦进入死亡状态,就不能再次启动

线程停止 stop

  1. 建议线程正常停止—>利用次数,不建议死循环
  2. 建议使用标志位
  3. 不要使用stop或destroy等过时或JDK不建议使用的方法
public class TestStop implements Runnable{
    //设置一个标志位
    private boolean flag=true;

    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("run...Thread"+i++);
        }
    }

    //设置一个公开方法停止线程,转换标志位
    public void stop(){this.flag=false;}

    public static void main(String[] args) {
        TestStop testStop = new TestStop();

        new Thread(testStop).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            if(i==900){
                testStop.stop();//让线程停止
                System.out.println("线程停止");
            }
        }
    }
}

线程休眠 sleep

  1. sleep(时间)指定当前线程阻塞的毫秒数
  2. sleep存在异常InterruptedException
  3. sleep时间达到后线程进入就绪状态
  4. 每一个对象都有一个锁,sleep不会释放锁

线程礼让 yield

  1. 礼让线程,让当前正在执行的线程暂停,但不阻塞
  2. 将线程从运行状态转为就绪状态
  3. 让CPU重新调度,礼让不一定成功!有可能执行礼让的线程又被CPU重新调度

线程强制执行 join

  1. join合并线程,待此线程执行完成再执行其他线程,其他线程阻塞

线程优先级 priority

  1. 优先级高的线程不一定先运行
  2. 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了
  3. 先设置优先级再启动线程
  4. 优先级范围为0~10,默认为5

守护线程 daemon

  1. 线程分为用户线程守护线程
  2. 虚拟机必须确保用户线程执行完毕
  3. 虚拟机不用等待守护线程执行完毕

线程同步 synchronized

同步方法

  • public synchronized void method(int args){}

  • 注意:若将一个大的方法申明为同步方法将会影响效率

public class UnsafeBuyying {
    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station,"张三").start();
        new Thread(station,"李四").start();
        new Thread(station,"王五").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNums = 10;
    boolean flag = true;//外部停止方式

    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    //synchronized 同步方法,锁的是this
    public synchronized void buy() throws InterruptedException {
        //判断是否有票
        if(ticketNums<=0){
            flag=false;
            return;
        }
        //模拟延时
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--);
    }
}

同步块

  • synchronized(Obj){}
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"私房钱");

        Drawing me =new Drawing(account,50,"我");
        Drawing friend =new Drawing(account,100,"朋友");

        me.start();
        friend.start();
    }
}

//账户
class Account{
    int money;
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread{
    Account account;
    int drawing;//取出的钱
    int now=0;//现在手上的钱

    @Override
    //取钱
    //synchronized 默认锁的是this.
    public void run() {
        //要锁的对象应是变化的量
        synchronized (account){
            //余额
            account.money-=drawing;
            //现在手上的钱
            now+=drawing;
            if(account.money-drawing<0){
                System.out.println(Thread.currentThread().getName()+"没有足够的余额");
                return;
            }
            //模拟延时,放大出错的概率
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println(account.name+"余额为:"+account.money);
            //因为继承了Thread类,在这里相当于Thread.currentThread().getName()
            //子类里无getName()同名方法,可以用this
            System.out.println(this.getName()+"现在手里的钱为:"+now);

        }

    }

    public Drawing(Account account, int drawing, String name){
        //Thread类中一个构造方法:传入一个字符串来改变线程的名字
        super(name);//super()只能写在构造方法第一行
        this.account=account;
        this.drawing=drawing;
    }
}

锁 Lock

  1. Lock是显式锁;synchronized是隐式锁,出了作用域自动释放

  2. Lock只有代码块锁,synchronized有代码块锁和方法锁

  3. 使用Lock锁,JVM将花费较少时间来调度线程,性能更好,并且有更好的拓展性

  4. 优先使用顺序:

    Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        Buying buying = new Buying();

        new Thread(buying).start();
        new Thread(buying).start();
        new Thread(buying).start();
    }
}

class Buying 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) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(ticketNums--);
                }else {
                    break;
                }
            }finally {
                lock.unlock();//解锁
            }
        }
    }
}

死锁 deadlock

  • 产生死锁的四个必要条件

    1. 互斥条件
    2. 请求与保持条件
    3. 不剥夺条件
    4. 循环等待条件
  • 例子:

    //死锁:多个线程互相占据对方需要的资源,形成僵持
    public class DeadLock {
        public static void main(String[] args) {
            Makeup g1 = new Makeup(0, "女一");
            Makeup g2 = new Makeup(1, "女二");
    
            g1.start();
            g2.start();
        }
    }
    
    //口红
    class LipStick{
    
    }
    
    //镜子
    class Mirror{
    
    }
    
    //化妆
    class Makeup extends Thread{
    
        //static保证资源只有一份
         static LipStick lipStick = new LipStick();
         static Mirror mirror = new Mirror();
    
         int choice;
         String girl;
    
        public Makeup(int choice, String girl) {
            this.choice = choice;
            this.girl = girl;
        }
    
        @Override
        public void run() {
            makeup();
        }
    
        //化妆,持有对方的锁
        private void makeup(){
            if (choice==0){
                synchronized (lipStick){//获得口红的锁
                    System.out.println(this.girl+"获得口红的锁");
    
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
    
                    synchronized (mirror) {//想要获得镜子
                        System.out.println(this.girl + "获得镜子的锁");
                    }
    
                }
    //            synchronized (mirror){//想要获得镜子
    //                System.out.println(this.girl+"获得镜子的锁");
    //            }
            }else {
                synchronized (mirror){
                    System.out.println(this.girl+"获得镜子的锁");
    
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
    
                    synchronized (lipStick){
                        System.out.println(this.girl+"获得口红的锁");
                    }
                }
    //            synchronized (lipStick){
    //                System.out.println(this.girl+"获得口红的锁");
    //            }
            }
        }
    }
    

生产者消费者问题

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

  • 在生产者消费者问题中,仅有synchronized是不够的
    • synchronized可阻止并发更新同一个共享资源,实现了同步
    • synchronized不能用来实现不同线程之间的消息传递(通信)

管程法

  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”,生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
//生产者消费者模型-->利用缓冲区解决:管程法
//生产者 消费者 产品 缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();

        new Producer(synContainer).start();
        new Consumer(synContainer).start();
    }
}

//生产者
class Producer extends Thread{
    SynContainer container;
    public Producer(SynContainer container){
        this.container=container;
    }

    //生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了"+i+"个产品");
            container.push(new Product(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++) {
            System.out.println("消费了"+container.pop().id+"个产品");
        }
    }
}

//产品
class Product{
    int id;
    public Product(int id){
        this.id=id;
    }
}

//缓冲区
class SynContainer{
    //设置容器大小
    Product[] products = new Product[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Product product){
        //如果容器满了,需要等待消费者消费
        while (count == 10) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有满,需要丢入产品
        products[count] = product;
        count++;
        //可以通知消费者消费
        this.notifyAll();
    }

    //消费者消费产品
    public synchronized Product pop(){
        //判断能否消费
        while (count == 0) {
            try {
                this.wait();//等待生产者生产
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Product product = products[count];
        //消费完了,通知生产者生产
        this.notifyAll();
        return product;
    }
}

信号灯法

//生产者消费者问题2:信号灯法,设置标志位
public class TestPC02 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Viewer(tv).start();
    }
}

//生产者-->演员
class Player extends Thread{
    TV tv;

    public Player(TV tv){
        this.tv=tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("唱跳rap");
            }else {
                this.tv.play("篮球");
            }
        }
    }


}

//消费者-->观众
class Viewer extends Thread{
    TV tv;

    public Viewer(TV tv){
        this.tv=tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.tv.watch();
        }
    }
}

//产品-->节目
class TV{
    //演员表演,观众等待 T
    //观众观看,演员等待 F
    String name;//表演的节目
    boolean flag = true;

    //表演
    public synchronized void play(String name){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("演员表演了:"+name);
        //通知观众观看
        this.notifyAll();
        this.name=name;
        this.flag=!flag;
    }

    //观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("观看了:"+name);
        //通知演员表演
        this.notifyAll();
        this.flag=!flag;
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值