多线程笔记(一)

什么是进程,线程:

这是个老生常谈的问题了,总结一下,看任务管理器,这里的进程就是所谓的进程,就是说一个程序运行起来就是一个进程,一个进程中可以包括多个线程。多个线程协同合作完成一个功能。
在这里插入图片描述

我们学习多线程主要学习什么内容呢?

1.线程的使用方式
2.线程的生命周期
3.线程的通信
4.线程的同步

下面我们来一一介绍这些

1.线程的使用方式

线程的创建方式以共有四种
**一,继承Thread类:**然后他有以下几个步骤
①继承Thread类
②重写run方法
③new一个子类,调用start方法

public class ThredTest
{
    public static void main(String[] args) {
        System.out.println("main before");
        myThread myThread = new myThread();
        myThread.start();
        System.out.println("main after");

    }
}
class  myThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            if(i%2==0){
            //设置当前线程名
                Thread.currentThread().setName("求偶数:");
               // 获取向前线程名
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}

在这里插入图片描述
结果不是顺序执行,而是执行完了main方法种的两个输出有执行的求偶数的方法,这就是多线程,通过start方法开启线程,调用run方法种的代码,main方法也是一个线程,这个时候这两个线程(一个是main方法,一个是子类的run方法等待CPU分配资源去执行,呗分配资源的概率是随机的,有可能每次都分给main方法,就像上面的执行结果)
二。实现Runnable接口
①实现Runnable接口
②重写方法
③将实现了Runnabe接口的子类的作为参数,new一个Thread()对象
④调用new的Thread类的start

public class ThredTest
{
    public static void main(String[] args) {
        System.out.println("main before");
        myRunnable myRunnable = new myRunnable();
        Thread testRunnable = new Thread(myRunnable);
        testRunnable.start();
        System.out.println("main after");

    }
}

class myRunnable implements Runnable{

    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            if(i%2!=0){
                Thread.currentThread().setName("求奇数:");
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}

在这里插入图片描述
看结果还是多线程运行的
**思考一个问题:**如果我不调用start方法,而是调用run方法会不会同样能够开启多线程

public static void main(String[] args) {
        System.out.println("main before");
        myRunnable myRunnable = new myRunnable();
        Thread testRunnable = new Thread(myRunnable);
        //调用run方法
        testRunnable.run();
        
        System.out.println("main after");

    }

在这里插入图片描述
这可不是巧合 ,是运行多次都是这一个结果,那为什么呢?原因是start方法种有开启线程的方法,方法里面默认会调用方法,如果只是调用run方法们就跟面向对象种调用子类的方法一个概念都是单线程的。

**再思考另一个问题:**为什么实现Runnable接口的的类也需要将逻辑代码写在run方法种,又为什么需要将实现了Runnable接口的类当作参数传给Thread类的构造器方法。那就看下源码
在这里插入图片描述
start方法种调用了start0()方法
在这里插入图片描述
如果target不为空,就调用target的run方法,如果是继承Thread类的方法(就是第一种实现多线程的方法)就会调用自己重写的run方法,这里是实现Runnble接口的方式。好的那么target是什么呢,看下Thread类的构造方法。
在这里插入图片描述
target就是作为参数传给构造方法的实现了Runnable方法的类的实例
**总结:**如果是继承了Thread,run方法是子类重写的方法,如果是实现接口的,就是target(实现接口的类的实例)的run方法,总的来说都是自己重写的方法方法

最后看一个好完了:Thread也是实现了Runnable接口
在这里插入图片描述
是不是很有意思,这就让我们更好的理解继承和接口的使用了,java支持单继承多实现,接口其实也是为了解决java单继承的问题出现的,想要具有更多的功能方法,再单继承的原则下,只能是实现接口。

三。实现Callable接口
①创建一个类实现Callable接口
②实现call方法
③new一个FutureTask,构造函数的参数是实现类Callable接口的类
④new 一个Thread类构造函数的构造函数的参数是FutureTask类的实例
⑤调用start方法

public class ThredTest {
    public static void main(String[] args) {
        TestCallable t=new TestCallable();
        FutureTask f=new FutureTask(t);
        new Thread(f).start();
        try { 
            Object o=f.get();
            System.out.println(o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class TestCallable implements Callable{

    @Override
    public Object call() throws Exception {
        int sum=0;
        for (int i=1;i<100;i++){
            if(i%2==0){
                System.out.println(Thread.currentThread().getName()+i);
                sum+=i;
            }
        }
        return sum;
    }
}

注意: call()方法是带返回值的并且是可以抛异常的,如果需要获取call方法的返回值,就调用FutureTask的get()方法就可以,但是要处理异常try{}catch{},其实FutureTask也实现了Runnable接口

四。使用线程池
在实际的公司种很少手动创建线程,都是使用线程池,因为手动的,使用线程池,需要开启线程的时候,直接去线程池里面拿,用完放回线程池,效率比较高,可以避免频繁创建销毁、实现重复利用。

corePoolSize:核心池的大小
maximumPoolSize:最大线程数  keepAliveTime:线程没有任务时最多保持多长时间后会终止

JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
 void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
void shutdown() :关闭连接池
Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池
Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。

public class ThredTest {
    public static void main(String[] args) {
        myRunnable myRunnable = new myRunnable();
        TestCallable testCallable = new TestCallable();
        FutureTask f=new FutureTask(testCallable);
        ExecutorService service=Executors.newFixedThreadPool(10);
        ThreadPoolExecutor executor= (ThreadPoolExecutor)service;
        System.out.println(service.getClass());
        service.submit(f);
        service.execute(myRunnable);
        service.shutdown();

    }
}

//设置线程池参数
public static void main(String[] args) {
        myRunnable myRunnable = new myRunnable();
        TestCallable testCallable = new TestCallable();
        FutureTask f=new FutureTask(testCallable);
        ExecutorService service=Executors.newFixedThreadPool(10);
        ThreadPoolExecutor executor= (ThreadPoolExecutor)service;
        executor.setCorePoolSize();
        executor.setKeepAliveTime();
        executor.setMaximumPoolSize();
        executor.setThreadFactory();
        executor.setRejectedExecutionHandler();
        System.out.println(service.getClass());
        service.submit(f);
        service.execute(myRunnable);
        service.shutdown();

    }
Thread类的一些其他方法

(一)。 yield()线程让步,首先他是一个静态方法,通过Thread.yield()方法调用,是的调用方法的线程进入就绪状态,方法的初衷是将cpu资源让给优先级更高,或者优先级相同的线程去执行,但是他是将线程的状态转换为了就绪状态,所以有可能还是继续调用自己,没有达到理想效果。

public class ThredTest {
    public static void main(String[] args) {
        myRunnable myRunnable = new myRunnable();
        myThread myThread=new myThread();
        Thread mt=new Thread(myRunnable);
        myThread.start();
        mt.start();

    }
}

class myRunnable implements Runnable{

    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            if(i%2!=0){
                if(i==20){
                    Thread.yield();
                }
                Thread.currentThread().setName("求奇数:");
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}



class  myThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            if(i%2==0){
                if(i==40){
                    Thread.yield();
                }
                Thread.currentThread().setName("求偶数:");
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
}

刚开始是交替执行,后来求偶数执行一直到i40,cpu交给求奇数执行,但是当i20的时候,求奇数让出cpu资源后又被自己强占到了,继续执行。
在这里插入图片描述
在这里插入图片描述
(二)。join方法, 线程插队,当一个线程种调用join方法,使另一个线程插队执行的时候,该线程处于阻塞状态,当插队的线程执行完成以后,该线程变成就绪状态,去抢占资源执行。(这里有两个点需要注意,一个是join的方法全部执行完,其他资源才可以执行,另一个注意的点就是,其他别插队的线程,是先阻塞状体,在就绪状态。)

public class ThredTest {
    public static void main(String[] args) {
        myRunnable myRunnable = new myRunnable();
        myThread myThread = new myThread();

        Thread mt = new Thread(myRunnable);
        mt.start();
        myThread.start();

    }
}

class myRunnable implements Runnable {

    @Override
    public void run() {
        ThreadThree threadThree = new ThreadThree();
        threadThree.start();
        System.out.println("开启第三个线程");
        for (int i = 1; i <= 100; i++) {
            if (i % 2 != 0) {
                try {
                    threadThree.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            Thread.currentThread().setName("求奇数:");
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}



class myThread extends Thread {
    @Override
    public void run() {


        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {

                
                Thread.currentThread().setName("求偶数:");
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
}

class ThreadThree extends Thread {
    @Override
    public void run() {


        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {

                Thread.currentThread().setName("求偶数Three:");
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
}

这里证明一个问题,当存在三个及以上线程时,其中一个线程被join方法插队,插队的方法继续和没有插队的方法竞争cpu,插队的方法只是会影响被插队的方法,不会影响其他的方法,就是说被插队的方法要等到插队的方法全部执行完才执行,但是插队的方法不是优先级最高的,他还要和其他线程(除了别插队的线程)竞争cpu去执行。就好像食堂排队打饭,其中一队有人插队,他只会影响有人插队的那一队,不会影响其他的队伍,这时候你就是cpu 决定要排那一队,排有人插队的也可以,怕其他队也可以。(希望我说明白了)
在这里插入图片描述
(三)。sleep()方法: 休眠多少时间,单位毫秒,休眠的时候阻塞状态,睡眠结束就绪状态,等待竞争cpu资源后,进行执行,通过Thread.sleep()调用,并且要处理异常。
(四)。stop(): 强制线程生命期结束,不推荐使用
(五)。boolean isAlive():返回boolean,判断线程是否还活着

线程的调度

在这里插入图片描述

(此处引用尚硅谷课程老师的课件中的内容,如有侵权,请联系删除,祝天下没有难学的技术)

线程的优先级

在这里插入图片描述
(此处引用尚硅谷课程老师的课件中的内容,如有侵权,请联系删除,祝天下没有难学的技术)

线程的生命周期

线程的生命周期也是十分重要的一个知识点,设计到线程之间的调度问题,如图。
在这里插入图片描述
需要注意的是,当new完一个线程的时候,就进入了新建状态,调用start()方法后,进入就绪状态,进入就绪转台以后就可以抢占cpu资源然后去执行了,就绪状态和运行状态是可以直接相互转换的。运行到阻塞,阻塞到就绪是单向的不能相互转换的。阻塞状态最后都是重新回到就绪状态然后,抢占cpu执行权然后执行的,都要经过就绪状态

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值