☀️苏州程序大白一文解析Java多线程☀️《❤️记得收藏❤️》

Posted 苏州程序大白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了☀️苏州程序大白一文解析Java多线程☀️《❤️记得收藏❤️》相关的知识,希望对你有一定的参考价值。

☀️苏州程序大白一文解析Java多线程☀️《❤️记得收藏❤️》

目录

🏳️‍🌈开讲啦!!!!🏳️‍🌈苏州程序大白🏳️‍🌈

🌟博主介绍

💂 个人主页:苏州程序大白

🤟作者介绍:中国DBA联盟(ACDU)成员,CSDN全国各地程序猿(媛)聚集地管理员。目前从事工业自动化软件开发工作。擅长C#、Java、机器视觉、底层算法等语言。2019年成立柒月软件工作室。

💬如果文章对你有帮助,欢迎关注、点赞、收藏(一键三连)和C#、Halcon、python+opencv、VUE、各大公司面试等一些订阅专栏哦

🎗️ 承接各种软件开发项目

💅 有任何问题欢迎私信,看到会及时回复

👤 微信号:stbsl6,微信公众号:苏州程序大白

🎯 想加入技术交流群的可以加我好友,群里会分享学习资料

基本概念

程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。

进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期。

线程:进程中的一个任务,是一个程序内部的一条执行路径

即:线程《 进程(一个进程可以有多个线程)

程序:静态的代码

  • 进程:动态执行的程序。

  • 线程:进程中要同时干几件事时,每一件事的执行路径成为线程。

  • 并行:多个CPU同时执行多个任务,比如 : 多个线程一起执行。

  • 并发:一个CPU(采用时间片)同时执行多个任务,比如:多个线程间隔执行。

线程的相关Api

		Thread thread = new Thread();
        thread.start();          //启动当前线程,调用当前线程中的run方法,注意并不是立即执行,只是进入就绪状态等待资源的调用
        thread.sleep(5000); 	 //休眠5s
        thread.getName();        //获取当前线程的名字,也可以Thread.currentThread().getName()
        thread.setName("");      //设置当前线程的名字,也可以Thread.currentThread().setName()
        Thread.currentThread();  //返回当前线程
        Thread.yield();          //主动释放当前线程的执行权
        thread.join();           //执行过程中插入该线程,只有插入线程执行完毕,主线程才会继续执行
        thread.isAlive();        //判断线程是否存活
        thread.setPriority(1);   //设置线程的优先级,有三个等级: MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5
        thread.getPriority();    //获取线程的优先级
        thread.setDaemon(false); //设置守护线程,主线程结束守护线程也结束 false,true
        thread.isDaemon();       //判断是否是守护线程
        thread.wait();           //强行停止当前线程,进入阻塞状态
        thread.notify();         //唤醒一个被wait停止线程的线程
        thread.notifyAll();      //唤醒所有被wait停止的停止线程

多线程和单线程

单线程示例: ​ ​

public class SingleThread {
 
    public void method1(){
        System.out.println("SingleThread.method1");
    }
 
    public void method2(){
        method1();
    }
 
    public static void main(String[] args) {
		//一条线执行
        SingleThread single = new SingleThread();
        single.method2();
 
    }
 
}

多线程示例: ​ ​

public class ManyThread extends Thread{
 
    @Override
    public void run() {
 
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
 
    }
 
    public static void main(String[] args) {
 
        ManyThread manyThread = new ManyThread();
        new Thread(manyThread,"线程A").start();
        new Thread(manyThread,"线程B").start();
 
    }
}

线程的创建

继承Thread类

1、创建一个继承Thread类的子类 (通过ctrl+o 快速找到run方法)。

2、重写Thread类的run()方法。

3、创建Thread子类的对象。

4、通过此对象调用start()方法。

public class ManyThread extends Thread{
 
    @Override
    public void run() {
 
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
 
    }
 
    public static void main(String[] args) {
 
        ManyThread manyThread = new ManyThread();
        manyThread.start();
        
    }
}

实现Runnable接口

1、创建一个实现了Runnable接口的类。

2、实现类去实现Runnable中的抽象方法:run()。

3、创建实现类的对象。

4、将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象。

5、通过Thread类的对象调用start()。

public class TestRunable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
        }
    }
 
    public static void main(String[] args) {
 
        TestRunable runable = new TestRunable();
        new Thread(runable).start();
 
    }
}

比较创建线程的两种方式: ​ ​ ​

开发中,优先选择实现Runable接口的方式。

原因: ​ ​ ​

1、适合多个相同的程序代码的线程去处理同一个资源。

2、可以避免java中的单继承的限制。

3、增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

​ 联系:Thread也是实现Runnable接口,两种方式都需要重写run()方法,将线程要执行的逻辑声明在run中。

​ 实现callable接口

1、创建一个实现了callable接口的类。

2、实现call方法,将此线程需要执行的操作声明在call()中。

3、创建实现类的对象。

4、将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象。

5、将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(调用FutureTask对象的get方法获取返回值)。

public class TestCallable implements Callable {
    @Override
    public Object call() throws Exception {
 
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
        }
 
        return "线程执行完毕";
    }
 
    public static void main(String[] args) throws ExecutionException, InterruptedException {
 
        TestCallable callable = new TestCallable();
        FutureTask<String> task = new FutureTask<String>(callable);
        new Thread(task).start();
        System.out.println(task.get());
 
    }
 
}

线程的生命周期


1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

​ (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

​ (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

​ (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

线程同步

线程安全问题

线程安全问题是指,多个线程对同一个共享数据进行操作时,线程没来得及更新共享数据,从而导致另外线程没得到最新的数据,从而产生线程安全问题。

解决方法:使用同步监视器(锁)synchronized 、lock(ReentrantLock)

同步代码块: ​ ​ ​ ​

public class ManyThread extends Thread{
    private static int count = 100;  //一百张票,共同的资源
    @Override
    public void run() {
        //同步代码块
        synchronized (this){
            while (count > 0) {
                try {
                    Thread.sleep(1000);  //休眠一秒
                    System.out.println(Thread.currentThread().getName()+"卖出了一张票,余票"+(--count));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) {
        ManyThread manyThread = new ManyThread();
        new Thread(manyThread,"窗口1").start();
        new Thread(manyThread,"窗口2").start();
        new Thread(manyThread,"窗口3").start();
        new Thread(manyThread,"窗口4").start();
    }
}

同步方法: ​ ​ ​ ​

public class ManyThread extends Thread{
 
    private static int count = 100;  //一百张票,共同的资源
    //创建ReentrantLock锁对象
    private Lock lock = new ReentrantLock();
    //使用synchronized同步方法
    @Override
    public synchronized void run() {
 
            while (count > 0) {
                try {
                    Thread.sleep(1000);  //休眠一秒
                    System.out.println(Thread.currentThread().getName()+"卖出了一张票,余票"+(--count));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }
 
    public static void main(String[] args) {
 
        ManyThread manyThread = new ManyThread();
        new Thread(manyThread,"窗口1").start();
        new Thread(manyThread,"窗口2").start();
        new Thread(manyThread,"窗口3").start();
        new Thread(manyThread,"窗口4").start();
 
    }
}

lock锁的使用: ​ ​ ​ ​

public class ManyThread extends Thread{
 
    private static int count = 100;  //一百张票,共同的资源
    //创建ReentrantLock锁对象
    private Lock lock = new ReentrantLock();
    
    @Override
    public void run() {
            lock.lock();
            while (count > 0) {
                try {
                    Thread.sleep(1000);  //休眠一秒
                    System.out.println(Thread.currentThread().getName()+"卖出了一张票,余票"+(--count));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            lock.unlock();
    }
 
    public static void main(String[] args) {
 
        ManyThread manyThread = new ManyThread();
        new Thread(manyThread,"窗口1").start();
        new Thread(manyThread,"窗口2").start();
        new Thread(manyThread,"窗口3").start();
        new Thread(manyThread,"窗口4").start();
 
    }
}

Synchronized与lock的区别

相同:二者都可以解决线程安全问题。

不同:synchronized机制在执行完相应的代码逻辑以后,自动的释放同步监视器lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())(lock的方式更为灵活)。

线程死锁

​ ​ 理解:两个人过独木桥,走到桥中央谁都不让谁。 ​ ​ ​ ​

public class Deadlock {
 
    public static void main(String[] args) {
        //准备两把锁
        Object o1 = new Object();
        Object o2 = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                //先拿第一把锁
                synchronized (o1){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("o1过了一半");
                    synchronized (o2){
                        System.out.println("o1过完桥了");
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                //先拿第一把锁
                synchronized (o2){
                    System.out.println("o2过了一半");
                    synchronized (o1){
                        System.out.println("o2过完桥了");
                    }
                }
            }
        }).start();
    }
}

解决死锁的办法: ​ ​ ​ ​ ​

1、减少同步共享变量。

2、采用专门的算法,多个线程之间规定先后执行的顺序,规避死锁问题。

3、减少锁的嵌套。

线程通信

​ 线程间通信可以通过共享变量+wait()+notify()来实现。

​ wait()将线程进入阻塞状态,notify()将线程唤醒,notifyAll()唤醒所有的线程。

使用前提:这三个方法均只能使用在同步代码块或者同步方法中:

​ wait 和 sleep的区别?

​ 二者都会让线程进入阻塞状态,有以下区别:

wait是Object的方法 sleep是Thread的方法。

wait会立即释放锁 sleep不会释放锁。

wait后线程的状态是Watting sleep后线程的状态为 Time_Waiting。

​ 生产者消费者模型:指的是有生产者来生产数据,消费者来消费数据,生产者生产满了就不生产了,通知消费者取,等消费了再进行生产。

public class Test {
 
    public static void main(String[] args) {
 
        Factory msg = new Factory();
        Produce produce = new Produce(msg);
        Consumer consumer = new Consumer(msg);
        new Thread(produce,"工厂A").start();
        new Thread(produce,"工厂B").start();
        new Thread(produce,"工厂C").start();
 
        new Thread(consumer,"店铺D").start();
        new Thread(consumer,"店铺E").start();
 
    }
 
}
 
/**
 * 消息
 */
以上是关于☀️苏州程序大白一文解析Java多线程☀️《❤️记得收藏❤️》的主要内容,如果未能解决你的问题,请参考以下文章

☀️苏州程序大白一文教你学会微信小程序开发☀️《❤️记得收藏❤️》

☀️苏州程序大白一文从基础手把手教你Python数据可视化大佬☀️《❤️记得收藏❤️》

☀️苏州程序大白解析Linux 中的虚拟网络接口☀️《❤️记得收藏❤️》

☀️苏州程序大白解析Linux 中的虚拟网络接口☀️《❤️记得收藏❤️》

苏州程序大白用2万字解析数据结构和八大排序算法☀️《❤️记得收藏❤️》

☀️苏州程序大白用万字解析Python网络编程与Web编程☀️《❤️记得收藏❤️》