Java——多线程

1、多线程使用

1、创建线程

2、调用线程对象的start方法来启动线程、
线程首先是需要去争夺CPU资源,当拿到资源之后才能执行任务

java 复制代码
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i + "=====");
        }
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }
}
2、实现 Runnable 接口

第一种形式线程和任务的耦合度过高,不利于程序的扩展,所以开发中不推荐使用

  • 实现解耦合
java 复制代码
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i + "===");
        }
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        thread1.start();
    }
}
线程的状态

线程一共有5种状态,在特定的情况下,线程可以在不同状态之间切换

  • 创建状态:实例化了一个新的线程对象,还未启动
  • 就绪状态:创建好的线程对象调用了 start() 方法完成启动,进入线程池等待抢占CPU资源
  • 运行状态:线程对象获取了CPU资源,在规定时间内执行任务
  • 阻塞状态:正在运行的线程暂停执行任务,释放所占用的CPU资源
  • 终止状态:线程运行完毕或因为异常导致线程终止运行
lambda表达式
  • 使用内部类简化代码
java 复制代码
public class Main {
    public static void main(String[] args) {
        MyRunnable3 myRunnable3 = new MyRunnable3();
        Thread thread = new Thread(myRunnable3);
        thread.start();
    }

    static class MyRunnable3 implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(i + "=====");
            }
        }
    }
}
  • 使用匿名内部类简化代码
java 复制代码
public class Main {
    public static void main(String[] args) {
        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(i + "====");
                }
            }
        };

        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}
  • 使用 lambda 表达式简化代码
java 复制代码
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(i + "====");
            }
        });
        thread.start();
    }
}
  • 将接口作为参数传递的场景下,可以使用 lambda 进行简化

接口里只能有一个方法

java 复制代码
public interface MyRunnable3 {
    public void test();
}
java 复制代码
public class MyTest {
    public void test(MyRunnable3 myRunnable3) {
        myRunnable3.test();
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        MyTest myTest = new MyTest();
        myTest.test(() -> {
            System.out.println(111);
        });
    }
}
3、线程休眠

休眠是指让当前线程暂停执行,从运行状态进入阻塞状态,从而将CPU资源让给其他线程的一种调度方式,通过 sleep 方法来实现
不是谁调用 sleep 就让谁休眠,而是看在哪调的,在哪个线程中调用 sleep 方法就让哪个线程休眠,和调用者无关

java 复制代码
public class Main {
    public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                if (i == 5) {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.println(i + "====");
            }
        }).start();
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(i + "Child子线程");
            }
        });

        thread.start();

        try {
            thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 100; i++) {
            System.out.println(i + "Main主线程");
        }
    }
}
4、线程调度
  • 线程合并

合并是指将指定的某个线程加入到当前线程中,合并为一个线程。
由两个线程交替执行变成一个线程中的两个子线程顺序执行,一个线程执行完毕之后再来执行第二个线程,通过 join 方法来实现合并。
线程A和线程B,线程A在执行到某个时间点的时候调用线程B的 join 方法,则表示从当前时间点开始CPU资源被线程B独占,线程A进入阻塞状态,直到线程B执行完毕,线程A继续获取CPU资源执行自己的任务。
谁调用 join ,就将谁合并到对方的线程中

java 复制代码
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(i + "子线程");
            }
        });
        
        thread.start();

        for (int i = 0; i < 100; i++) {
            if (i == 10) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(i + "主线程");
        }
    }
}
  • join 方法存在重载 join(long mills)

通过 join 进行合并,则合并进来的线程会一直占用CPU资源,直到自己的任务结束才会释放CPU,另外一个线程才能执行
通过 join(long mills) 进行合并,则合并进来的线程不会一直占用CPU资源,根据 mills 时间来占用,时间到了之后无论线程是否执行完毕,都会将CPU释放出来,回到两个线程争夺CPU资源的情况

java 复制代码
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(i + "Child子线程");
            }
        });

        thread.start();

        for (int i = 0; i < 100; i++) {
            if (i == 10) {
                try {
                    thread.join(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(i + "Main主线程");
        }
    }
}
5、线程同步

多线程同时访问同一个资源,可能导致数据不准确的情况

java 复制代码
public class Account implements Runnable {
    private static int num;

    @Override
    public synchronized void run() {
        num++;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "是当前的第" + num + "位访客");
    }
}

synchronized 可以修饰实例方法,也可以修饰静态方法,但两者有区别

  • 修饰静态方法
java 复制代码
public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(() -> {
                test();
            });
            thread.start();
        }
    }

    public synchronized static void test() {
        System.out.println("===start");

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("end=====");
    }
}

如何判断多个线程是否会实现同步,看锁定的资源在内存中有几份,多线程肯定需要排队,如果有多份,则多线程不需要排队,就不会实现同步

  • 修饰代码块
java 复制代码
public class Main {
    public static void main(String[] args) {
        Main main = new Main();

        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(() -> {
                main.test();
            });
            thread.start();
        }
    }

    public void test() {
        synchronized (this) { // Main 只new了一份,所以会同步
            System.out.println("===start");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("end=====");
        }
    }
}
  • 锁定常量

包装类常量池有一个可用范围:-128~127,在此区间内常量池可用,同一个数值可以给多个线程使用,一旦超出这个范围,则常量池失效,此时就需要单独在堆中创建对应的数据,不是一个对象而是多个对象

java 复制代码
public class Main {
    public static void main(String[] args) {
        Main main = new Main();

        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(() -> {
                main.test(127);
            });
            thread.start();
        }
    }

    public void test(Integer value) {
        synchronized (value) {
            System.out.println("===start");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("end=====");
        }
    }
}
6、线程安全的单例模式
java 复制代码
public class Account {
    private static Account account;

    private Account() {
        System.out.println("创建了Account对象");
    }

    public synchronized static Account getInstance() {
        if (account == null) {
            account = new Account();
        }

        return account;
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                Account account = Account.getInstance();
            }).start();
        }
    }
}
7、死锁
java 复制代码
public class DeadLockRunnable implements Runnable {
    public int user;
    private static Object kuaizi1 = new Object();
    private static Object kuaizi2 = new Object();

    @Override
    public void run() {
        // 1号拿着筷子1,试图获取筷子2
        // 2号拿着筷子2,试图获取筷子1
        if (user == 1) {
            System.out.println(Thread.currentThread().getName() + "获取到筷子1,等待获取筷子2");
            synchronized (kuaizi1) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                synchronized (kuaizi2) {
                    System.out.println(Thread.currentThread().getName() + "拿到两根筷子,用餐完毕");
                }
            }
        }

        if (user == 2) {
            System.out.println(Thread.currentThread().getName() + "获取到筷子2,等待获取筷子1");
            synchronized (kuaizi2) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                synchronized (kuaizi1) {
                    System.out.println(Thread.currentThread().getName() + "拿到两根筷子,用餐完毕");
                }
            }
        }
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        DeadLockRunnable deadLockRunnable1 = new DeadLockRunnable();
        deadLockRunnable1.user = 1;

        DeadLockRunnable deadLockRunnable2 = new DeadLockRunnable();
        deadLockRunnable2.user = 2;

        new Thread(deadLockRunnable1, "张三").start();
        new Thread(deadLockRunnable2, "李四").start();
    }
}
8、重入锁

ReentrantLock 是对 synchronized 的升级, synchronized 是通过JVM实现,ReentrantLock是通过JDK实现(用方法调用的形式来使用)
重入锁的特点是可以重复上锁
synchronized 是自动上锁,自动解锁,ReentrantLock 是手动上锁,手动解锁

java 复制代码
public class Account implements Runnable {
    private static int num;
    private ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        reentrantLock.lock();
        num++;
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + "是当前第" + num + "位访客");
        reentrantLock.unlock();
    }
}

ReentrantLock 还具备限时性的特点,指可以判断某个线程在一定时间内能否获取到锁

  • tryLock(long timeout, TimeUnit unit)
java 复制代码
public class Account implements Runnable {
    private ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        try {
            if (reentrantLock.tryLock(3, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread().getName() + "获取到了锁");
                Thread.sleep(5000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (reentrantLock.isHeldByCurrentThread()) {
                reentrantLock.unlock();
            }
        }
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        Account account = new Account();
        new Thread(account, "张三").start();
        new Thread(account, "李四").start();
    }
}
相关推荐
天若有情6731 小时前
从零搭建校园二手交易系统:Vue2 + SpringBoot 4.x 全栈实战
java·spring boot·后端
lexiangqicheng1 小时前
1,自定义表单通俗理解-PC设计APP渲染
java·前端·javascript
南极企鹅2 小时前
生产环境gc文件分析
java·jvm·算法
Wang's Blog2 小时前
Java 项目实战: 交友项目 - 前后端分离的工程实践
java·spring cloud·架构
溪语流沙2 小时前
面试加分项!Manacher:O(n)求最长回文子串的对称之美
java·python·leetcode·面试·职场和发展
知守观2 小时前
Maven SNAPSHOT 更新排查:-U 无效、clean 无用,问题在 _remote.repositories(附完整链路)
java·后端·maven
牛战士coding2 小时前
从一句\“取消订单\“到退款通知:AI 写操作完整业务闭环
java·人工智能
啵啵啵鱼2 小时前
第三章 栈、队列和数组相关结构
java·数据结构·栈·队列