javaEE初阶————多线程初阶(1)

多线程初阶————

1,认识线程

1.1 概念

1)线程是什么

线程就是一个“执行流”,可以理解为程序执行的最小单位;

可以看成轻量级的进程;

2)为啥要有线程

“并发编程” 的需要,但是我们不是已经有进程了吗,我们要知道,我们进行的是服务器开发,我们在访问网站的时候,一个用户进行访问就是一个进程,用户的数据量是非常庞大的,进程的创建和销毁需要的开销就会变得非常非常大,这样我们就引出了线程,让一个进程中包含一个或多个进程,提升效率,

3)线程和进程的区别

1,进程是操作系统进行资源分配的基本单位,线程是操作系统进行运算调度的基本单位;

2,线程的创建,销毁,调度需要的开销更小;

3,进程之间互不影响,同一进程下的线程会互相影响,创建进程后会自动创建一个线程,第一个线程会涉及到申请资源的操作,其余线程不会涉及,进程销毁才会释放资源,线程的销毁不会释放资源;

4,因为线程是调度相关,所以每一份线程都有调度相关的数据

5,一个进程死掉了不会影响其他进程,但是一个进程中一个线程死掉了就掀桌了,全部都运行不了了;

4)java中线程和操作系统的关系

java中包装好了操作系统中对线程操作的API,但是java是不推荐多进程编程的,我们只去学习多线程编程;

1.2 第一个多线程程序

class MyThread extends Thread{
    public void run(){
        System.out.println("myThread");
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();

        System.out.println("main");
    }
}

 main就是进程刚创建我们自动生成的第一个线程,运行

只是个示范,看接下来的讲解就好;

1.3 创建线程

1)创建对象继承Thread类
class MyThread2 extends Thread{
    public void run(){
        while(true){
            System.out.println("MyThread2 线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new MyThread2();
        
        System.out.println("主线程");
    }
}

我们创建一个类,让它继承Thread类,Thread类中给我们提供了一个run方法,让我们自己去写里面的内容,我们就在自己实现的类中重写run方法,我们循环打印,并且打印一次睡眠1秒,在主线程也就是main方法中,打印主线程,我们来看运行结果;

 

只有一个主线程,因为我们没有去调度线程,我们可以直接用.run或者是.start来开启线程

thread.run();

程序一直在运行,这里不明显,我们来借助一个工具,

找到jdk中的bin ,

以管理员身份运行它

找到我们刚才创建的Demo2

点击线程

我们看到main线程一直在等待,因为我们使用的run方法,所以是在主线程上运行的,如果我们想看到我们自己创建的线程,就要用start

我们在试试;

这个Thread——0就是我们自己创建的线程,但是main呢,还有为啥先打印的主线程呢,因为调度随机的,我们不知道操作系统让拿个线程先执行,所以就会发生这样的状况,这也是我们后期要重点掌握的,要怎么保证线程之间协调配合,避免乌鸦哥掀桌,哈哈哈,main线程在这里已经结束了,没啥好说的了,下一个; 

2)实现Runnable接口
class MyRunnable implements Runnable{
    public void run(){
        while(true){
            System.out.println("MyRunnable 线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo3 {
    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
        System.out.println("主线程");
    }
}

这是第二种,我们Runnable接口,我们还是要使用Thread类来创建,这不有病吗.........这么麻烦,还不如用第一种,其实这种想法是不对的,大家听,没听过,高内聚低耦合,这里就谈到了低耦合,我们使用接口,在想要修改的时候去修改接口的代码即可,是不影响Thread的,但是我们使用Thread的时候,想要修改的时候,就要修改Thread中的代码, 可能扯到线程相关的代码,而且用类继承一次局限性大,接口更灵活;

运行

我们这次让主线程也活着


class MyRunnable implements Runnable{
    public void run(){
        while(true){
            System.out.println("MyRunnable 线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

 来运行

这次更能看到随机调度的现象

3)匿名内部类(Thread)

这几个其实用的都少,最多用到的还是lambda表达式

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(){
            public void run(){
                while(true){
                    System.out.println("Thread 线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        
        
        thread.start();
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

跟之前都一样,就是使用·匿名内部类了;

直接看运行

4)匿名内部类(Runnable)
public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread =  new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("Thread 线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        thread.start();
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

一样嗷,匿名内部类创建Ruunable对象,

5)lambda表达式

这个才是我们使用最多的方法,主要是很方便;

lambda表达式:

(参数)->{实现了啥}

public class Demo6 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
           while (true){
               System.out.println("Thread 线程");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
        });
        
        thread.start();
        
        while(true){
            System.out.println("主线程");
            Thread.sleep(1000);
        }
    }
}

 方便吧,在new Thread的时候直接在括号中使用lambda表达式就行;

大家可能有疑问,不说重写run方法吗,这个{}里面的就是我们已经重写了,这个跟那个第三个匿名内部类的方法其实很像的;

我们来看运行结果;

完美嗷

2,Thread类及常见方法

Thread类是JVM用来管理线程的一个类,我们每创建一个Thread对象就有一个线程与他对应;

2.1 Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象并命名
Thread(Runnable target,String name)使用Runnable

 Thread thread1 = new Thread();
        Thread thread2 = new Thread(new Runnable() {public void run() {}});
        Thread thread3 = new Thread(()->{
            while (true){
                System.out.println("线程3");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"线程3");
        
        Thread thread4 = new Thread(new Runnable() {public void run() {}},"线程4");

我们用4种构造方法创建了线程,我们来观察一下线程3,我们是否把线程的名字修改了呢;

 

 成功看到线程3了;

2.2 Thread的常见属性

属性获取方法
ID

getId()

名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrputed()

1) ID 类似进程的pid,线程的唯一标识,不同线程不会重复;

2) 名称 各种调试工具用到;

3) 线程当前所处的情况;

3) 通常来说优先级高的线程会容易调用;

4) 可以想象为饭局中的小程序员,对这次饭局不起决定性作用,JVM会在一个进程的所有非后台线程结束后结束;

我们可以使用SetDaemon()来把当前线程设置为后台线程;

5) run方法是否结束;

6) 终止线程运行;

我们来写一个代码获取所以线程信息;

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "还活着");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "即将死亡");
        },"线程1");


        System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());
        System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());
        System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());
        System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());
        System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());
        System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());

        thread.start();
        while (thread.isAlive()){
            System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());
            System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());
            System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());
            System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());
            System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());
            System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());

            System.out.println(Thread.currentThread().getState() + " " + thread.getState());
            Thread.sleep(1000);
        }
        System.out.println("ID :" + Thread.currentThread().getId() + "     "  + "ID :" + thread.getId());
        System.out.println("name :" + Thread.currentThread().getName() + "     "  + "name :" + thread.getName());
        System.out.println("state :" + Thread.currentThread().getState() + "     "  + "state :" + thread.getState());
        System.out.println("优先级 :" + Thread.currentThread().getPriority() + "     "  + "优先级 :" + thread.getPriority());
        System.out.println("是否存活 :" + Thread.currentThread().isAlive() + "     "  + "是否存活 :" + thread.isAlive());
        System.out.println("中断? :" + Thread.currentThread().isInterrupted() + "     "  + "中断? :" + thread.isInterrupted());

    }
}

 运行之后就能看到整个过程了;

2.3 启动一个线程

我们之前用过run方法来启动线程,实际上着并不是真正创建了线程,我们使用start真正在操作系统底层创建了一个线程,只有创建了线程对象再start才是让线程真正独立执行了;

2.4 中断一个线程

线程一旦工作就会等到任务结束才会停下来,但是有时候我们有让线程立即停下的需求,我们有两种办法来中断一个线程,其实叫终止更好,因为不是间断,而是线程就结束了;

我们来模拟一个场景,有两个员工张三,李四,老板让他们去给别人转账,张三正在转给骗子,李四及时阻止;

1,共享标记来中断线程

public class Demo3 {
    public static boolean a = true;
    public static void main(String[] args) {
        Thread thread1 = new Thread(()->{
            while (a){
                System.out.println(Thread.currentThread().getName() + "正忙着转账呢");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "我嘞个豆,差点转走了");
        },"张三");

        Thread thread2 = new Thread(()->{
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "老板来电话了!" + "张三在给骗子转账!");
                a  = false;
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"李四");

        thread1.start();
        thread2.start();
    }
}

来看运行结果 

哈哈哈哈哈,好玩吧; 

2,调用interrupt()方法来通知

方法说明
Thread对象.interrupt()中断对象关联的线程,如果线程正在阻塞,以异常方式通知,否则设置标志位
public static boolean interrputed();判读当前线程的标志位是否设置,调用后清除标志位;
public boolean 判读当前线程的标志位是否设置,调用后不清除标志位;

在Java线程的上下文中,中断标志位是Thread对象维护内部的布尔值用于表示该线程是否被请求中断。 

public class Demo3 {
    public static void main(String[] args) {
        Thread thread1 = new Thread(()->{
            //或者用Thread.interrupted();
            while (!Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName() + "正忙着转账呢");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "我嘞个豆,差点转走了");
        },"张三");

        Thread thread2 = new Thread(()->{
            try {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName() + "老板来电话了!" + "张三在给骗子转账!");
                thread1.interrupt();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"李四");

        thread1.start();
        thread2.start();
    }
}

这里的原理就一样了,但是代码运行会报一个异常,

这个是因为

thread1.interrupt();

 唤醒了sleep让他直接抛出InterruptedException,被捕获到,抛出RuntimeException异常,所以我们在这里直接break就行;

这样结果就对了;

2.5 等待一个线程

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等待millis毫秒
public void join(long millis, int nanos)等待线程结束,精度更高;后面是纳秒;

 

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"张三");

        Thread thread2 = new Thread(()->{
            while (true){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"李四");

        thread1.start();
        thread1.join();

        thread2.start();
        thread2.join();

        System.out.println("全部线程打印结束");
    }
}

我们创建了两个线程和主线程,thread1.join意思为让主线程等待thread1线程执行完再执行 ,此时thread2还没开启,我们来看运行结果

 

 

张三始终在工作我天,因为我们使用的join没有放参数,是无休止的等待;如果我们放参数就不会这样傻傻的等待了, 

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(()->{
            while (!Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        },"张三");

        Thread thread2 = new Thread(()->{
            while (!Thread.interrupted()){
                System.out.println(Thread.currentThread().getName() + "线程正在工作");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        },"李四");

        thread1.start();
        thread1.join(2000);
        thread1.interrupt();

        thread2.start();
        thread2.join(2000);
        thread2.interrupt();

        System.out.println("全部线程打印结束");
    }
}
");

修改一下代码。。。。。 

 

我们这回看到第一个join先让thread1插队,thread1运行2毫秒后,主线程启动,设置中断标志位,thread1停止,thread2插队,等待两毫秒后嗝屁,主线程也结束了; 

2.6 获取当前线程的引用

 这个之前我们就使用过了;

方法说明
public static Thread currentThread() 返回当前对象的引用,类似this

没啥好说的嗷,来段代码就好了;

public class Demo2 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            System.out.println(Thread.currentThread().getName());
        },"线程1");

        thread.start();
        System.out.println(Thread.currentThread().getName());

    }
}

 运行结果

2.7 休眠当前线程

这个也没啥好说的,我们一直在使用

方法说明
public static void sleep (long millis) throws InterputedException 休眠当前线程millis毫秒
public static void sleep (long millis,int nanos) throws InterputedException 更高精度

不演示了嗷,马上下一期

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值