多线程

进程

-	是指一个内存中运行的应用程序,每个进程都有一个独立的空间(都有各自的堆栈,不会共享)

线程:

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行。一个进程最少有一个线程。
  • 线程实际上是在进程基础上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。

线程调度

分时调度

-	所有线程轮流使用CPU使用权,平均分配每个线程占用CPU的时间

抢占式调度

-	优先让优先级高德线程使用CPU,如果现成的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
-	CPU使用抢占式调度模式多个	

同步与异步

同步:排队执行,效率低,但是安全。

异步:同时执行,效率高,但是数据不安全。

并发与并行

并发:指两个或多个事件在同一个时间段内发生。

并行:指两个或多个事件在同一个时刻发生(同时发生)。

线程常见的两种方式,继承Thread 和 实现Runnable

public class Demo01 {
    /**
     * 多线程技术
     * @param args
     */
    public static void main(String[] args) {
        //Thread
        MyThread m = new MyThread();
        //开启一个线程
        m.start();
        //主线程执行
        for(int i=0;i<10;i++){
            System.out.println("main方法" + i);
        }
    }
}


public class MyThread extends Thread{

    /**
     * run方法就是线程要执行的方法
     */
    @Override
    public void run() {
        //这里的代码是新的执行路径
        //此时的执行路径的触发方式,不是调用run方法,而是通过thread对象的star()来启动任务
        for (int i=0;i<10;i++){
            System.out.println("run方法实现" + i);
        }
    }
}

每个线程都拥有自己的栈空间,共用一份堆内存

/**
 * 多线程技术
 * 实现Runnable 与 继承Thread相比有如下优势:
 *  1.  通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况。
 *  2.  可以避免单继承所带来的局限性,(Java中不允许多继承,但是可以多实现)
 *  3.  任务与线程本身是分离的,提高了程序的健壮性
 *  4.  后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
 */
public class Demo01 {
    /**
     * 多线程技术
     * @param args
     */
    public static void main(String[] args) {
        //实现Runnable
        //1.    创建一个任务对象
        MyRunnable myRunnable = new MyRunnable();
        //2.    创建一个线程,并分配一个任务
        Thread thread = new Thread(myRunnable);
        //3.    执行这个线程
        thread.start();

        for(int i=0;i<10;i++){
            System.out.println("main中的方法(主线程)" + i);
        }
    }
}


/**
 * 用于给线程执行的任务
 */
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        //线程的任务
        for (int i=0;i<10;i++){
            System.out.println("Runnable --这里的线程 " + i);
        }
    }
}

有时使用Thread比较简洁,

public class Demo02 {
    /**
     * 匿名内部类
     * @param args
     */
    public static void main(String[] args) {
        //使用Thread其有很简洁的写法
        //创建一个对象,通过匿名内部类来开辟一个新线程,只调用一次方法
        new Thread(){
            @Override
            public void run() {
                for(int i=0;i<10;i++){
                    System.out.println("Thread这里开辟的线程 " + i);
                }

            }
        }.start();//只调用一次.start()方法

        for(int i=0;i<10;i++){
            System.out.println("main中的方法(主线程)" + i);
        }
    }
}

设置和获取线程名称

public class Demo01 {
    /**
     * 多线程技术
     * @param args
     */
    public static void main(String[] args) {
        //如何获取  当前 线程的名称
        System.out.println(Thread.currentThread().getName());
        //如何设置线程名称
        new Thread(new MyRunnable(),"名称XX").start();
        //获取线程名称
       TThread t = new Thread(new MyRunnable());
        t.start();
        //获取线程名称
        new Thread(new MyRunnable()).start();

    }

    static class MyRunnable implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

线程休眠sleep

public class Demo01 {
    /**
     * 多线程技术
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        //线程的休眠 sleep
        for (int i=0;i<10;i++){
            System.out.println("run方法实现" + i);
            //1000毫秒,线程的指定时间休眠
            Thread.sleep(1000);
        }

    }
}

线程阻塞

线程阻塞不是sleep,简单理解为:所有消耗时间或者比较消耗时间的操作。常见的文件读取,代码等待在那个位置等文件读完,或者等待用户输入,我们常称为耗时操作

线程的中断

public class Demo04 {
    public static void main(String[] args) {
        //线程的中断
        //一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定
        //匿名
        //new Thread(new MyRunnable()).start();
        Thread t1 = new Thread(new MyRunnable());
        t1.start();
        for (int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
            //每隔1000毫秒,打印一次
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        //给线程t1增加中断标记,
        t1.interrupt();
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for (int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName() + ":" + i);
                //每隔1000毫秒,打印一次
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {//线程中断的异常
                    //e.printStackTrace();
                    System.out.println("发现中标记,线程自杀");
                   return;
                }
            }
        }
    }
}

守护进程

public class Demo04 {
    public static void main(String[] args) {
        //线程:分为守护线程和用户线程
        //用户线程:当一个 进程 不包含任何存活的用户线程时,进行结束。
        //守护线程:守护用户线程的,当最后一个用户线程结束后,所有守护线程自动死亡

        Thread t1 = new Thread(new MyRunnable());
        //直接创建的线程是用户线程,
        //设置为守护线程,一定要在启动之前去设置,设置 t1为主线程
        t1.setDaemon(true);
        
        t1.start();
        for (int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName() + ":" + i);
            //每隔1000毫秒,打印一次
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for (int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName() + ":" + i);
                //每隔1000毫秒,打印一次
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {//线程中断的异常
                    e.printStackTrace();
                }

            }
        }
    }
}

线程安全和不安全

public class Demo05 {
    public static void main(String[] args) {
        //线程不安全
        //创建一个任务,多态
        Runnable runnable = new Ticket();

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

    /**
     * 创建任务类,创建一个任务对象,交给多个线程执行
     */
    static class Ticket implements Runnable{
        //票数
        private int counts = 10;

        @Override
        public void run() {
            //只要票没有卖完就接着卖
            while(counts>0){
                //卖票
                System.out.println("正在准备卖票:");
                //休眠1秒,放大卖票的时间,放大出错的几率
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                counts--;
                System.out.println("出票成功,余票:" + counts);
            }
        }
    }
}

***************************************************************
    
"D:\Program Files\Java\jdk-14.0.2\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1\lib\idea_rt.jar=63022:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1\bin" -Dfile.encoding=UTF-8 -classpath D:\Users\ASUS\IdeaProjects\Demo\out\production\Demo com.kkb.demo.Demo05
正在准备卖票:
正在准备卖票:
正在准备卖票:
出票成功,余票:8
正在准备卖票:
出票成功,余票:9
正在准备卖票:
出票成功,余票:7
正在准备卖票:
出票成功,余票:6
正在准备卖票:
出票成功,余票:5
正在准备卖票:
出票成功,余票:4
正在准备卖票:
出票成功,余票:2
正在准备卖票:
出票成功,余票:3
正在准备卖票:
出票成功,余票:1
正在准备卖票:
出票成功,余票:-1
出票成功,余票:0
出票成功,余票:-2

Process finished with exit code 0

线程安全:

public class Demo05 {
    /**
     * 线程同步:synchronized
     * @param args
     */
    public static void main(String[] args) {
        //线程不安全
        //解决方案一:同步代码块
        //格式:   synchronized(锁对象){}
        Runnable runnable = new Ticket();

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

    /**
     * 创建任务类,创建一个任务对象,交给多个线程执行
     */
    static class Ticket implements Runnable{
        //票数
        private int counts = 10;
        private Object o = new Object();//多个线程用一把锁才能排队
        @Override
        public void run() {
            //Object o = new Object();错误示范,
            //应该是多个线程看一个锁对象,而不是各自的锁对象
            //只要票没有卖完就接着卖
            while(true){
                synchronized (o){
                    if (counts>0){
                        //卖票
                        System.out.println("正在准备卖票:");
                        //休眠1秒,放大卖票的时间
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        counts--;
                        System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + counts);
                    }else {
                        break;
                    }
                }
            }
        }
    }
}

*********************************************************
"D:\Program Files\Java\jdk-14.0.2\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1\lib\idea_rt.jar=63343:D:\Program Files\JetBrains\IntelliJ IDEA 2020.1\bin" -Dfile.encoding=UTF-8 -classpath D:\Users\ASUS\IdeaProjects\Demo\out\production\Demo com.kkb.demo.Demo05
正在准备卖票:
Thread-0出票成功,余票:9
正在准备卖票:
Thread-0出票成功,余票:8
正在准备卖票:
Thread-0出票成功,余票:7
正在准备卖票:
Thread-0出票成功,余票:6
正在准备卖票:
Thread-0出票成功,余票:5
正在准备卖票:
Thread-0出票成功,余票:4
正在准备卖票:
Thread-0出票成功,余票:3
正在准备卖票:
Thread-2出票成功,余票:2
正在准备卖票:
Thread-1出票成功,余票:1
正在准备卖票:
Thread-1出票成功,余票:0

Process finished with exit code 0