zoukankan      html  css  js  c++  java
  • Java多线程

    1.1 线程概述

    进程:每个进程都有独立的代码和数据空间,进程是资源分配的最小单位,每个独立的程序占有一个进程

    线程:表示程序的执行流程,是CPU调度执行的基本单位,同一类线程共享代码和数据空间,进程是线程的容器,即一个进程包含1+n个线程

    多线程:每个线程完成一个功能,并与其他线程在同一个进程中并发执行,这种机制被称为多线程

    多线程的优点:让程序更好的利用系统资源,不但能更好的利用系统的空闲时间,还能快速对用户的请求作出响应,使程序的运行效率大大提高,也增加了程序的灵活程度;最重要的是,可通过多线程技术解决不同任务之间的协调操作与运行、数据交互、资源分配等难题;在某些情况下使程序设计更简单。

    1.2 线程的创建

    实现多线程编程的方式主要有两种:

    一种是继承Thread类

    另一种是实现Runnable接口

     

    继承Thread类:

    package com.se.Thread;
    
    // 子线程,继承Thread类,重写run()方法
    //创建线程方式一:继承Thread类,重写run方法,调用start开启线程
    //总结:注意,线程开启不一定立即执行,有CPU调用
    public class MyThread extends Thread{
        @Override
        public void run() {
            // 这个线程要干的事!
            for (int i = 0; i < 100; i++) {
                System.out.println(this.getName()+"-->MyThread run:"+i);
            }
        }
    }
    
    // 测试类
    class Test02{
        public static void main(String[] args) {
            // 创建Thread子类对象
            MyThread myThread = new MyThread();
            // 给线程设置名字
            myThread.setName("我们的第一个子线程");
            // 开启线程(子线程)
            myThread.start();
    
            // 开启主线程
            for (int i = 0; i < 100; i++) {
                System.out.println("mian run:"+i);
            }
        }
    }

    实现Runnable接口:

    package com.se.Thread;
    
    // 子线程,继承Runnable接口,重写run()方法
    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 子线程要执行的任务
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "-->MyRunnable run:" + i);
            }
        }
    }
    
    // 测试类
    class Test03 {
        public static void main(String[] args) {
            // 创建Runnable实现类的对象
            MyRunnable runnable = new MyRunnable();
            // 以此对象作为参数构造Thread类
            Thread thread = new Thread(runnable);
            // 给线程设置名字
            thread.setName("我们的第二个子线程");
            // 开启线程(子线程)
            thread.start();
    
            // 开启主线程
            for (int i = 0; i < 100; i++) {
                System.out.println("mian run:" + i);
            }
        }
    }

    1.3 两种创建线程方式的对比

    使用继承自Thread类的方式创建多线程:

    优点:编写简单,如果需要访问当前线程,直接使用 this 即可

    缺点:继承自Thread类了,不能再继承其他类

     

    使用实现Runnable接口的方式创建多线程:

    优点:还可以继承自其他类

    缺点:编写稍微复杂,访问当前线程,需使用 Thread.currentThread() 方法

    package com.kuangshen.demo02;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.concurrent.*;
    
    //线程创建方式三:实现callable接口
    public class TestCallable implements Callable<Boolean> {
        private String url;
        private String name;
    
        public TestCallable(String url, String name) {
            this.url = url;
            this.name = name;
        }
    
        @Override
        public Boolean call() throws Exception {
            WebDownloader webDownloader = new WebDownloader();
            webDownloader.downloader(url,name);
            System.out.println("下载的文件名为:"+name);
            return true;
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            TestCallable testCallable1 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_360_360%2Fbb%2F37%2Ff5%2Fbb37f583e8da88aed385306a07361c2a.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1629440173&t=03523962fee34ed4d7f9d7b0e1de3f2f", "1.jpg");
            TestCallable testCallable2 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_360_360%2Fbb%2F37%2Ff5%2Fbb37f583e8da88aed385306a07361c2a.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1629440173&t=03523962fee34ed4d7f9d7b0e1de3f2f", "2.jpg");
            TestCallable testCallable3 = new TestCallable("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_360_360%2Fbb%2F37%2Ff5%2Fbb37f583e8da88aed385306a07361c2a.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1629440173&t=03523962fee34ed4d7f9d7b0e1de3f2f", "3.jpg");
            //创建执行服务:
            ExecutorService ser = Executors.newFixedThreadPool(3);
            //提交执行
            Future<Boolean> r1 =  ser.submit(testCallable1);
            Future<Boolean> r2 = ser.submit(testCallable2);
            Future<Boolean> r3 =  ser.submit(testCallable3);
            //获取结果
            Boolean rs1 = r1.get();
            Boolean rs2 = r2.get();
            Boolean rs3 = r3.get();
            System.out.println(rs1);
            System.out.println(rs2);
            System.out.println(rs3);
            //关闭服务
            ser.shutdownNow();
        }
    }
    class WebDownloader{
        //下载方法
        public void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    1.4 线程的静态代理模式

    静态代理模式总结:
    真实对象和代理对象都要实现同一个接口
    代理对象要代理真实角色

    好处:
    代理对象可以做很多真实对象做不了的事情
    真实对象专注做自己的事情
    
    
    package com.kuangshen.demo02;
    
    //静态代理模式总结;
    //真实对象和代理对象都要实现同一个接口
    //代理对象要代理真实角色
    
    //好处:
    //代理对象可以做很多真实对象做不了的事情
    //真实对象专注做自己的事情
    
    public class StaticProxy {
        public static void main(String[] args) {
            You you = new You(); //你要结婚
            new Thread(() -> System.out.println("我爱你")).start();
            new WeddingCompany(new You()).happyMarry();
    //        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("结婚之后,收尾款");
        }
    }
    
    

    1.5 Lamda表达式

    总结:
      lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。

      前提是接口为函数式接口
      多个参数也可以去掉参数类型,要去掉都去掉,必须加括号
    package com.kuangshen.demo02;
    
    /**
     * 推到lambda表达式
     */
    public class TestLambdal {
        //3.静态内部类
        static class Like2 implements ILike {
    
            @Override
            public void lambda() {
                System.out.println("i like lambda2");
            }
        }
    
        public static void main(String[] args) {
            ILike like = new Like();
            like.lambda();
            ILike like2 = new Like2();
            like2.lambda();
            //4.成员内部类
            class Like3 implements ILike {
    
                @Override
                public void lambda() {
                    System.out.println("i like lambda4");
                }
            }
            ILike like3 = new Like3();
            like3.lambda();
    
            //5.匿名内部类,没有类的名称,必须借助接口或者父类
            like = new ILike() {
                @Override
                public void lambda() {
                    System.out.println("I like lambda5");
                }
            };
            like.lambda();
    
            //6.用lambda简化
            like = ()->{
                System.out.println("I like lambda6");
            };
            like.lambda();
        }
    }
    
    //1.定义一个函数式接口
    interface ILike {
        void lambda();
    }
    
    //2.实现类
    class Like implements ILike {
    
        @Override
        public void lambda() {
            System.out.println("i like lambda");
        }
    }
     

    1.6 线程的生命周期

    新建状态:当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值

    就绪状态:当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行

    运行状态:如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态

    阻塞状态:当处于运行状态的线程失去所占用资源之后,便进入阻塞状态

    死亡状态:线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。

    1.4 线程的优先级

    进程中至少有一个线程,在众多线程中,有时需要优先执行一个线程,这个线程的优先级越高,得到CPU执行的机会就越大,反之就越小

    线程的优先级使用1-10之间的整数来表示,数字越大,优先级越高;除了通过整数表示优先级,还可以通过Thread类的3个静态常量表示:MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY

    package com.se.Thread;
    
    public class MyThread2 extends Thread{
        public MyThread2(String name){
            super(name);
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                System.out.println("当前线程名字:"+this.getName()+"/优先级:"+this.getPriority()+"/循环到第几次:"+i);
            }
        }
    }
    package com.se.Thread;
    
    public class Test05 {
        public static void main(String[] args) {
            System.out.println("当前线程名字:"+Thread.currentThread().getName()+"/优先级:"+Thread.currentThread().getPriority()+"/主线程:");
            //创建子线程
            MyThread2 myThread1 = new MyThread2("第一个子线程");
            MyThread2 myThread2 = new MyThread2("第二个子线程");
            MyThread2 myThread3 = new MyThread2("第三个子线程");
            MyThread2 myThread4 = new MyThread2("第四个子线程");
            //设置优先级
            myThread1.setPriority(Thread.MIN_PRIORITY); // 1
            myThread2.setPriority(Thread.NORM_PRIORITY);// 5
            myThread3.setPriority(6);
            myThread4.setPriority(Thread.MAX_PRIORITY); // 10
    
            myThread1.start();
            myThread2.start();
            myThread3.start();
            myThread4.start();
        }
    }

    1.5 线程的控制

    sleep()方法,来使正在执行的线程以指定的毫秒数暂停,进入睡眠状态,在该线程休眠时间内,CPU会调度其他线程

    package com.se.Thread;
    
    public class MySleepThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(this.getName()+"输出数字:"+i);
                if (i % 2 == 0) {
                    try {
                        Thread.sleep(500); //当前线程休眠500毫秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    package com.se.Thread;
    
    public class Test06 {
        public static void main(String[] args) {
            //创建一个子线程
            MySleepThread mySleepThread = new MySleepThread();
            //启动子线程
            mySleepThread.start();
    
            //主线程的代码
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName() + "输出数字:" + i);
                if (i % 3 == 0) {
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    yield()方法,来暂停当前正在执行的线程对象,把机会让给相同或优先级更高的线程,因此, yield()方法也称为线程让步

    package com.se.Thread;
    
    public class MyYeildThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println(this.getName() + "循环次数:" + i);
                if (i == 10) {
                    System.out.println("当前线程执行了10次," + this.getName() + "我先让步");
                    Thread.yield(); //让步
                }
            }
        }
    }
    package com.se.Thread;
    
    public class Test07 {
        public static void main(String[] args) {
            MyYeildThread thread1 = new MyYeildThread();
            MyYeildThread thread2 = new MyYeildThread();
    
            thread1.start();
            thread2.start();
        }
    }

    join()方法,来实现让某个线程插队这一功能。当线程调用join()方法时,线程会进入阻塞状态,直到join方法加入的线程执行完毕,该线程才会继续执行

    package com.se.Thread;
    
    public class MyJoinThread extends Thread {
        @Override
        public void run() {
            System.out.println(this.getName() + "线程开启...");
            for (int i = 0; i < 20; i++) {
                System.out.println(this.getName() + "--->" + i);
            }
            System.out.println(this.getName() + "线程关闭...");
        }
    }
    package com.se.Thread;
    
    public class Test08 {
        public static void main(String[] args) {
            MyJoinThread thread1 = new MyJoinThread();
            MyJoinThread thread2 = new MyJoinThread();
    
            thread1.start();
            thread2.start();
    
            try {
                thread1.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                thread2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程执行完毕!");
        }
    }

    1.6 线程的同步

    在多线程中,为了处理共享资源竞争,Java可以使用同步机制;

    所谓同步机制,指的是两个线程同时作用在一个对象上,应该保持对象数据的统一性和整体性。

    Java中提供了关键字synchronized,可以处理多线程同步竞争资源的问题

     

     

     

    案例演示:

    有1500张从北京西到郑州的z63车次的车票,售票1000张,退票500张,利用多线程中线程同步
    package com.se.syncPackage;
    
    public class Tickets {
        public static final Object lock = new Object(); //
        public static int bjz2zz = 1500; // 有1500张从北京西到郑州的z63车次的车票
    
        /**
         * 售票
         */
        public synchronized void sale(){
            bjz2zz--;
        }
    
        /**
         * 退票
         */
        public synchronized void refound(){
            bjz2zz++;
        }
    }
    
    
    package com.se.syncPackage;
    
    public class SaleTickets extends Thread {
        private Tickets tickets;
    
        public SaleTickets(Tickets tickets) {
            this.tickets = tickets;
        }
    
        @Override
        public synchronized void run() {
            for (int i = 0; i < 1000; i++) {
                //售票
                tickets.sale();
            }
        }
    }
    package com.se.syncPackage;
    
    public class RefoundTickets extends Thread {
        private Tickets tickets;
    
        public RefoundTickets(Tickets tickets) {
            this.tickets = tickets;
        }
    
        @Override
        public synchronized void run() {
            for (int i = 0; i < 500; i++) {
                // 退票
                tickets.refound();
            }
        }
    }
    package com.se.syncPackage;
    
    public class Test01 {
        public static void main(String[] args) {
            Tickets tickets = new Tickets();
            //售票的子线程
            SaleTickets saleTickets = new SaleTickets(tickets);
            //退票的子线程
            RefoundTickets refoundTickets = new RefoundTickets(tickets);
    
            saleTickets.start();
            refoundTickets.start();
    
            try {
                saleTickets.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                refoundTickets.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Tickets.bjz2zz);
        }
    }

    1.7 线程的停止

    总结:
    1.建议线程正常停止 --》 利用次数,不建议死循环

    2.建议使用标志位 --》 设置一个标志位
    3.不要使用stop或者destory等果实或者jdk不建议使用的方法
    package com.kuangshen.state;
    
    //测试stop
    //1.建议线程正常停止 --》 利用次数,不建议死循环
    //2.建议使用标志位 --》 设置一个标志位
    //3.不要使用stop或者destory等果实或者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++);
            }
        }
        //2.设置一个公开的方法停止线程
        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){
                    //调用stop方法切换标志位,让线程停止
                    testStop.stop();
                    System.out.println("线程停止");
                }
            }
        }
    }

    1.8 线程的状态

    Thread thread = new Thread();
    thread.getState();//获取线程当前的状态
    Thread.State.NEW //新生状态
    Thread.State.RUNNABLE // 运行状态
    Thread.State.TERMINATED  //终止状态
    package com.kuangshen.state;
    
    //观察测试线程的状态
    public class TestState {
        public static void main(String[] args) {
            Thread thread = new Thread(() -> {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("===========");
            });
    
            //观察状态
            Thread.State state = thread.getState();
            System.out.println(state);
    
            //观察启动后
            thread.start();//启动线程
            state = thread.getState();
            System.out.println(state);
    
            while (state != Thread.State.TERMINATED) { //只要线程不终止,就一直输出状态
                try {
                    Thread.sleep(100);
                    state = thread.getState(); // 更新线程状态
                    System.out.println(state);// 输出状态
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    1.9 线程的死锁

    所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

    在多线程中,形成死锁的4个必要条件:

    1.互斥使用

    2.不可抢占

    3.请求和等待

    4.循环等待

    Java中的死锁是应该尽量避免的,避免死锁,只需要避免4个条件中的任意一个即可;

    常用的方法:

    1.避免嵌套锁

    2.设置锁定的顺序

    3.设置锁定等待的时间

     

     

     

     

    
    
    package com.se.deadLockPackage;
    
    public class Tickets {
        public static final Object lock = new Object(); //
        public static int bjz2zz = 1500; // 有1500张从北京西到郑州的z63车次的车票
    
        public static final Object lock2 = new Object(); //
        public static int bjz2gz = 1500; // 有1500张从北京西到广州的z63车次的车票
    
        /**
         * 售票
         */
        public void sale() {
            synchronized (lock) {
                bjz2zz--;
                // bjx2gz也减少了一张?
                synchronized (lock2) {
                    bjz2gz--;
                }
            }
        }
    
        /**
         * 退票
         */
        public void refound() {
            // 锁的先后顺序要一致,否则会出现线程的死锁
            synchronized (lock) {
                bjz2zz++;
                synchronized (lock2) {
                    bjz2gz++;
                }
            }
        }
    }
    
    
    package com.se.deadLockPackage;
    
    public class SaleTickets extends Thread {
        private Tickets tickets;
    
        public SaleTickets(Tickets tickets) {
            this.tickets = tickets;
        }
    
        @Override
        public synchronized void run() {
            for (int i = 0; i < 1000; i++) {
                // 售票
                tickets.sale();
            }
        }
    }
    package com.se.deadLockPackage;
    
    public class RefoundTickets extends Thread {
        private Tickets tickets;
    
        public RefoundTickets(Tickets tickets) {
            this.tickets = tickets;
        }
    
        @Override
        public synchronized void run() {
            for (int i = 0; i < 500; i++) {
                tickets.refound();
            }
        }
    }
    package com.se.deadLockPackage;
    
    public class Test01 {
        public static void main(String[] args) {
            Tickets tickets = new Tickets();
            SaleTickets saleTickets = new SaleTickets(tickets);
            RefoundTickets refoundTickets = new RefoundTickets(tickets);
            saleTickets.start();
            refoundTickets.start();
            try {
                saleTickets.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                refoundTickets.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Tickets.bjz2zz);
            System.out.println(Tickets.bjz2gz);
        }
    }
    
    
  • 相关阅读:
    linux多线程学习笔记五--线程安全【转】
    linux多线程学习笔记六--一次性初始化和线程私有数据【转】
    【Linux】可重入函数和线程安全的区别与联系【转】
    【Linux】自主实现my_sleep【转】
    Linux/Unix编程中的线程安全问题【转】
    C语言字符串操作总结大全(超详细)【转】
    linux中的strip命令简介------给文件脱衣服【转】
    FTK应用程序编程接口(API)手册-1【转】
    python编程(python开发的三种运行模式)【转】
    ftk学习记(label篇)【转】
  • 原文地址:https://www.cnblogs.com/LEPENGYANG/p/15038933.html
Copyright © 2011-2022 走看看