JavaSE-线程

一.基础概念

程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

进程:执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位。通常一个进程中包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位。进程就是运行起来的程序,操作系统会给进程分配空间

 线程:线程是进程创建的,是进程的一个实体,比如用迅雷下载文件,每开启一个下载任务,就是开启了一个进程。

单线程:同一个时刻,只允许执行一个线程。

多线程:同一个时刻,可以执行多个线程。

并发:计算机怎么做到并发有许多不同的形式,比如对于一个单核处理器,计算机可以通过分配时间片的方式,让一个任务执行一段时间,再切换到另一个任务再运行一段时间,不同的任务会交替往复的执行下去,这个过程也成为进程或线程的上下文切换,单核CPU实现的多任务就是并发。比如单核CPU的电脑同时打开qq和迅雷,看似同时执行,其实是cpu在来回切换。

并行:在不同的核心上真正并行的执行任务,而不用通过分配时间片的方式运行。多核cpu执行多个任务。一个cpu或者一核单独处理一个线程。

多线程不一定是并发也不一定是并行,需要看有多个线程的这个进程被分配到什么样的CPU,当分配到单核CPU就只能并发,而分配到核心个数大于线程个数的CPU就可以并行

并发是同时出发,并行是同时执行,并发是两个队交替去使用咖啡机,并行是两个队去使用两个咖啡机,并发是多个线程被多个CPU执行,并行是多个线程被一个CPU轮流执行,串行是一个任务没做完,下面的任务只能等着,也就是两个线程执行完一个才能去执行另一个

注意:很多多线程是模拟出来的,真正的多线程是指多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。

线程的创建和使用

方式1:继承Thread类

继承Thread类,重写run()方法,调用start()开启线程后,会由CPU调度运行run()方法里的代码

//1.继承Thread类
public class Demo extends Thread {
    //2.重写run()方法
    public void run(){
        for(int i = 0;i<20;i++)
        System.out.println("我是子线程"+i);
    }

    public static void main(String[] args) {
        //3.创建线程对象,调用start()方法开启线程,调用run()方法并不是开启线程,只是调用方法
        //开启线程后,两个线程输出的内容可能是交替的,体现出多个线程同时执行,线程不一定立即执行,由CPU调度
        Demo demothread = new Demo();
        demothread.start();
        //demothread.run();

        for(int i = 0;i<20;i++)
        System.out.println("我是主线程"+i);
    }
}

代码简述:通过继承Thread开启多线程,该类就可以当作线程使用,通过线程对象直接start()启动线程,不建议这种方法,为了避免OOP单继承局限性 ,因为类继承了Thread后就不能继承其他类了。

 

原理解析-线程问题

当运行程序后,就开启了一个进程。

执行main方法,就开启了一个main线程。

创建线程类,启动后,在main线程中又创建了一个子线程。

当主线程启动子线程后,主线程不会阻塞,会继续执行。

当所有的线程执行完后,也就是执行时间最长的线程结束后,进程也就结束了。

 原理解析-run和start

run方法是一个普通的方法,如果调用run方法,不开启子线程,会先执行完run方法,再执行main方法,不能同时执行,会导致main方法阻塞。

start方法才能真正启动线程。

start方法调用start0方法后,该线程并不一定立马执行,只是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU统一调度。所以start0方法才是真正实现多线程的方法

方式2:实现Runnable接口

如果一个类继承了其它类,就不能继承Thread类了,如果还想实现多线程,就需要借助Runnable接口 

//1.创建一个类实现Runnable接口
public class Demo2 implements Runnable{
    //2.重写run()方法
    public void run(){
        for(int i = 0;i<20;i++){
            System.out.println("我是子线程"+i);
        }
    }

    public static void main(String[] args) {
        //3.创建实现类对象
        Demo2 demoThread2 = new Demo2();
        //4.创建线程对象,通过线程对象开启线程,也就是说还是离不开Thread类,因为start()方法只在
        //Thread里,通过其他类重写的run()方法需要传进Thread对象里
        Thread thread = new Thread(demoThread2);
        thread.start();
        //new Thread(demoThread2).start();,可以将两行上面两行代码简写
        for(int i = 0;i<20;i++)
            System.out.println("我是主线程"+i);
    }
}

 代码简述:实现Runnable接口实现多线程,通过传入Thread类目标对象+start()方法开启线程,避免了单继承的局限性,灵活方便,通过向Thread传入不同的实现类对象,即该线程可以执行不同的任务。也可以创建一个实现类对象,这一个实现类对象可以传入多个new的Thread类对象里,即一个对象被多个线程使用。

 小结一下

继承Thread和实现Runnable接口本质都是得靠Thread方法才能实现多线程

介绍以下Thread类中的方法

int getPriority();                返回线程优先级

boolean isAlive();                确定线程是否在运行

static void sleep(ms);            使线程暂停执行

void setName();                   给线程设置名称

String getName();                 返回线程名称

void start();                     通过调用run()方法启动线程执行

static Thread currentThread();    返回当前正在执行的线程对象的引用

public final void join();         允许线程等待直到调用该方法的线程终止为止
    
void interrupt();                 用于使线程执行中断

static boolean interrupted();     用于确定当前线程是否被其他线程中断

 Alive()方法的使用情景

由于主线程与其它所以线程并行运行,不能确定哪个线程最后执行完,有时需要确保主线程最后一个执行结束,同时想知道哪个子线程是否执行结束(主线程即将结束时判断子线程是否结束),就需要借助Alive()和join()

public class Demo8 {
    public static void main(String[] args) {
        //创建对象即调用构造方法,所以直接开启了线程
        NewThreadClass obj = new NewThreadClass();
        //打破传统的印象,引用类型不一定都是对象,这里展示了引用类型当作属性时的调用情况
        System.out.println(obj.t+"is alive ? :"+obj.t.isAlive());

            try {
                for(int i = 1;i<=5;i++) {
                    System.out.println("Main Thread loop:" + i);
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                System.out.println("Main thread is interrupted");
            }
            
            System.out.println(obj.t.getName() + "is alive ?:"+obj.t.isAlive());
        System.out.println("Main Thread is exiting");
    }
}

class NewThreadClass implements Runnable{
    Thread t;
    NewThreadClass(){
        t = new Thread(this,"ChildThread");//将Runnable接口实现类对象传入
        System.out.println("Thread created:" + t.getName());
        t.start();
    }
    @Override
    public void run() {

            try {
                for(int i = 1;i<=5;i++){
                    System.out.println(t.getName() + "loop:"+i);
                    Thread.sleep(100);
                }
            }
             catch (InterruptedException obj) {
                    System.out.println("Thread:"+t.getName()+"interrupted");
            }
    }
}

Thread created:ChildThread
ChildThreadloop:1
Thread[ChildThread,5,main]is alive ? :true
Main Thread loop:1
ChildThreadloop:2
Main Thread loop:2
ChildThreadloop:3
ChildThreadloop:4
Main Thread loop:3
ChildThreadloop:5
Main Thread loop:4
Main Thread loop:5
ChildThreadis alive ?:false
Main Thread is exiting

join()方法的使用情景

join方法让主线程等待,直到子线程调用完成才继续执行,也可以在join()方法中指定等待的最长时间,指定时间过后,主线程不再等待,将继续执行。

当子线程被中断,join方法将抛出InterruptedException异常,一个线程的执行可以被另一个线程的interrupt()方法中断,所以要确定当前线程是否被另一个线程中断。

public class Demo9 {
    public static void main(String[] args) {
        ChildThread obj = new ChildThread();
        System.out.println(obj.t.getName()+"is alive?:"+obj.t.isAlive());
        try{
            System.out.println("Main thread waiting for child thread to finish");
            obj.t.join();//暂时理解为让t的线程优先执行
            for(int i = 1;i<=5;i++){
                System.out.println("Main thread loop:"+i);
            }
        }catch (InterruptedException e){
            System.out.println("Main thread is interrupted");
        }
        System.out.println(obj.t.getName()+"is alive?:"+obj.t.isAlive());
        System.out.println("Main Thread is exiting");
    }
}

class ChildThread implements Runnable{
    Thread t;
    ChildThread(){
        t = new Thread(this,"ChildThread");
        System.out.println("Thread created:"+t.getName());
        t.start();
    }
    @Override
    public void run() {
        try{
            for(int i = 1 ;i<=5;i++){
                System.out.println(t.getName()+"loop:"+i);
                Thread.sleep(500);
            }
        }catch (InterruptedException obj){
            System.out.println("Thread:"+t.getName()+"interrupted");
        }
    }
}

Thread created:ChildThread
ChildThreadloop:1
ChildThreadis alive?:true
Main thread waiting for child thread to finish
ChildThreadloop:2
ChildThreadloop:3
ChildThreadloop:4
ChildThreadloop:5
Main thread loop:1
Main thread loop:2
Main thread loop:3
Main thread loop:4
Main thread loop:5
ChildThreadis alive?:false
Main Thread is exiting

方式3:实现Callable接口

1.实现Callable接口,需要返回值类型

2.重写call方法,需要抛出异常

3.创建目标对象

4.创建执行服务,ExecutorService ser = Executors.newFixedThreadPool(1);

5.提交执行:Future<Boolean> result1 = ser.submit(t1);

6.获取结果:boolean r1 = result1.get();

7.关闭服务:ser.shuutdownNow();

好处:可以有返回值,可以抛出异常

public class DemoThread5 implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        DemoThread5 d1 = new DemoThread5();
        DemoThread5 d2 = new DemoThread5();
        DemoThread5 d3 = new DemoThread5();
        
        //创建执行服务,需要三个线程
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行,跟run一样
        Future<Boolean> r1 = ser.submit(d1);
        Future<Boolean> r2 = ser.submit(d2);
        Future<Boolean> r3 = ser.submit(d3);
        //获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r1.get();
        boolean rs3 = r1.get();
        //关闭服务
        ser.shutdown();
    }
}

代码简述:创建三个实现类对象,创建一个线程池,将三个实现类对象放入线程池,执行call方法再用get获取结果,最后关闭池子

线程的生命周期

新建状态-New

        new了线程之后

就绪状态-Runnable

        调用start()之后,此时Java虚拟机为当前线程创建了线程栈和程序计数器,new的时候没有

运行状态-Runnable

        经过CPU调度后会处于一个运行状态,当运行的线程被抢走了线程就会变成Runnable中的就绪状态

阻塞状态

        因为某些原因,暂时放弃CPU的使用权,即刻让出CPU的时间片,停止运行这个线程,直到线程再次进入到一个可运行状态,再次获取CPU的时间片,再去运行

        等待阻塞

                执行了wait()方法,JVM会把该线程放到等待队列中,直到其它线程调用Object类的notify()或notifyAll()方法后。

        同步阻塞-Block

                运行的线程在获取对象的同步锁(synchronized)时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(Lock pool)中,这个阻塞状态只针对被synchronized修饰时

        其他阻塞

                运行的线程执行Thread.sleep()或join()方法,或者发出了IO请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时join()等待线程终止超时或或者IO处理完毕时,线程重新转入可运行状态。在主线程中调用子线程的join()方法,可让主线程等待子线程执行完毕,然后进入可运行状态

死亡状态

                 当run()方法中的语句全部执行完毕,线程就进入死亡状态,将线程对象设为null值也会让其进入死亡状态,调用stop()方法也会杀死线程,线程一旦死亡,就彻底终止,不能够重写启动。Thread类中的isAlive()方法用于判断线程是否或者。

六种状态

New

Runnable:对应着就绪状态和运行状态

Blocked:等待排他锁,要执行某个方法被锁在外面

Waiting:只能等待唤醒信号才能被唤醒

Timed Waiting:有时间参数,超过规定时间会被自动唤醒,或者提前接收到了等待信号

Terminated 

解释一下阻塞状态

一般习惯而言,把Blocked(被阻塞),Watting(等待),Timed_Watting(计时等待)都称为阻塞状态

//展示线程的New,Runnable,Terminated状态,即使正在运行,也是Runnable状态,而不是Running
public class DemoThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i<1000;i++){
            System.out.println(i);
        }
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new DemoThread());
        System.out.println(thread.getState());//打印出New状态
        thread.start();
        System.out.println(thread.getState());//打印出Runnable状态
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //线程能够休眠肯定已经开始运行了,打印出Runnable,即使是正常运行也是Runnable,而不是Running
        System.out.println(thread.getState());
        //当sleep时间长点,就能打印出TERMINATED
    }
}

代码简述:展示状态NEW,RUNNABLE,TERMINATED

//展示Blocked,Waiting,TimedWaiting
public class DemoThread6 implements Runnable{
    @Override
    public void run() {
        syn();
    }
    private synchronized void syn(){
        try {
            Thread.sleep(1000);
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        DemoThread6 d = new DemoThread6();
        Thread t1 = new Thread(d);
        t1.start();
        Thread t2 = new Thread(d);
        t2.start();
        System.out.println("11");
        System.out.println("11");
        //此时t1线程执行sleep方法会进入TimedWaiting状态
        //t2拿不到锁,是Blocked状态
        System.out.println(t1.getState());
        System.out.println(t2.getState());

        try {
            Thread.sleep(1300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //打印出Watting状态,因为执行了wait方法而且没有被唤醒
        System.out.println(t1.getState());

    }
}

代码简述:展示BLOCKED,WATTING,TIMED_WATTING 三个状态

线程的优先级

由于CPU一个时间只能执行一个线程,每个线程都有一个优先级,执行就绪的各个线程将按照优先级排队,有较高优先级的线程会被先调用

定义线程优先级

线程优先级被定义为1-10之间的整数,CPU以指定顺序执行多个线程的过程成为调度。

如果处理器遇到优先级更高的其它线程,那么当前线程执行完成后,将执行具有较高优先级的线程

如果较高优先级的线程停止或变为不可运行或正在等待IO操作,那么下一个较低优先级的线程开始执行

设置线程优先级

通过Thread类的setPriority(int newPriority)

MAX_PRIORITY是10,MIN_PRIORITY是0,NORM_PRIORITY是5

如果参数值不在1-10中,则抛出IllegalArgumentException异常。

线程优先级高不代表一直执行这个线程,当优先级高的线程在执行时,其它线程已经排好队,所以会按照队列执行。优先级高的线程会先执行完

public class Demo8 {
    public static void main(String[] args) {

        NewThreadClass obj1 = new NewThreadClass(Thread.NORM_PRIORITY - 2);
        NewThreadClass obj2 = new NewThreadClass(Thread.NORM_PRIORITY + 2);
        NewThreadClass obj3 = new NewThreadClass(Thread.NORM_PRIORITY + 3);
        obj1.t.start();
        obj2.t.start();
        obj3.t.start();
        try {
            System.out.println("Main thread waiting foro child thread to finish");
            obj1.t.join();
            obj2.t.join();
            obj3.t.join();
        } catch (InterruptedException e) {
            System.out.println("Main thread is interrupted");
        }
        System.out.println(obj1.t + "is alive?:" + obj1.t.isAlive());
        System.out.println(obj2.t + "is alive?:" + obj2.t.isAlive());
        System.out.println(obj3.t + "is alive?:" + obj3.t.isAlive());
        System.out.println("Main Thread is exiting");
    }
}
class NewThreadClass implements Runnable{
    Thread t;
    NewThreadClass(int p){
        t = new Thread(this,"ChildThread");
        t.setPriority(p);
        System.out.println("Thread created:" + t);
    }
    @Override
    public void run() {

            try {
                for(int i = 1;i<=5;i++){
                    System.out.println(t + "loop:"+i);
                    Thread.sleep(500);
                }
            }
             catch (InterruptedException obj) {
                    System.out.println("Thread:"+t+"interrupted");
            }
    }
}

线程同步

线程的同步确保当两个或多个线程需要访问共享资源时,一次仅一个线程使用该资源

同步方法

当线程在同步方法内时,所有试图在同一时间调用方法的其它线程必须等待,在同步方法的执行过程中,它所属的对象被锁定,因此无法调用任何其它同步方法。当方法完成其执行时会自动释放监视程序,当线程正在休眠或等待时,它临时释放它所持有的锁。

同步语句

如果只是对方法内的一小段关键代码进行同步,也可以使用同步语句,而不是同步整个方法

线程通信

多用于生产者-消费者的场景,thead1将生产的数据放到容器中,thread2来使用

在线程通信中展示notify方法wait()notifyAll()使用

wait():通知当前线程离开其对监视程序的控制并等待,直到遇到其它线程调用notify()方法为止

notify():唤醒正等待对象的监视程序得到执行的单个线程。如果多个线程正在等待,则随机选择一个

notifyAll():唤醒正等待对象的监视程序的所有线程。

机制1

轮询,让消费者thread2重复检查容器中是否有数据,有就立即使用,如果没有就等待一段时间再次检查,直到容器中有了数据。

多线程在共享数据时,为了避免数据不一致,必须给数据加锁,某一个时间段只能有一个线程访问容器,那么轮询中的无效检查就对CPU时间产生了浪费。

机制2

消费者thread2去访问容器,当它发现数据没有准备好,就进入等待状态,并释放容器的锁,使得其它线程可以使用容器。

某个时刻生产者thread1访问容器并放入数据,在它释放锁之前,通知正在等待状态的消费者thread2去使用

对比

机制2中thread2在条件不满足时暂时等待,通知其它线程先使用,thread1使用后通知正在等待thread2,线程之间通过交流,减少了对共享容器的无效占用时间。

机制1中thread2一直在访问容器,其它线程无法使用容器,thread1优先级高,当thread1要使用容器时,thread2会让出。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值