线程

线程

1.进程与线程

进程是拥有资源的基本单位,线程是调度和分配的基本单位。
线程一般不拥有资源,但线程可以访问隶属进程的资源,
即一个进程所拥有的资源,可供该进程下的所有线程共享
进程的切换开销远大于线程的切换开销

2.多线程

多线程:是指这个程序(一个进程)运行时产生的多个线程

3.并行与并发

并行:多个CPU实例或者多台机器同时执行一段处理逻辑,是真正的同时
并发:通过CPU调度算法,让用户看上去同时执行,实际上从CPU操作层面并不是真正的同时。
并发往往在场景中有公用的资源

线程的状态

可参考链接:https://blog.csdn.net/xingjing1226/article/details/81977129

  • .新建(new):新创建了一个线程对象
  • 可运行(RUNNABLE):线程对象创建后,其他线程(main线程)调用该对象的start()方法。该状态的线程位于可运行线程池中等待被线程调度选中,获取CPU的使用权
  • 运行(RUNNING):可运行状态的线程获得了CPU的时间片,执行程序代码
  • 阻塞(BLOCKED):阻塞状态是因为线程因为某种原因放弃了CPU 的使用权,即让出了时间片,暂时停止运行,直到线程进入可运行状态,才有机会再次获得时间片进入运行状态
  • 死亡(DEAD):线程run()、main()方法执行结束,或者因为异常退出了run方法,则该线程结束生命周期。死亡的线程不可复生在一个死去的线程调用start()方法,会抛出java.lang.IllegalThreadStateException异常

阻塞的情况分为三种

  • 等待阻塞:运行的线程执行o.wait()方法,jvm会把该线程放入等待队列中
  • 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中
  • 其他阻塞:运行的线程执行Thread.sleep(long
    ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行状态

线程的五种状态图

在这里插入图片描述

一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程
在这里插入图片描述

线程的创建

java使用Thread类来代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用四种方式来创建线程,如下所示:

  1. 继承Thread类创建线程
  2. 实现Runnable接口创建线程
  3. 使用Callable和Future创建线程
  4. 使用线程池例如用Executor框架

线程创建的具体实现

------------------------继承Thread类创建线程---------------------
通过继承Thread类来创建并启动多线程的一般步骤如下
1】定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。

2】创建Thread子类的实例,也就是创建了线程对象

3】启动线程,即调用线程的start()方法
代码实现:

package com.example.demo.thread;

public class ThreadTest extends Thread
{
    @Override
    public void run()
    {
        System.out.println("我在创建线程");
    }
    public static void main(String[] args){
        //创建并启动线程
        new ThreadTest().start();
    }
}

----------------------通过实现Runnable接口创建并启动线程----------------------------

1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体

2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象

3】第三部依然是通过调用线程对象的start()方法来启动线程

代码实现
package com.example.demo.thread;

public class RunableTest implements Runnable
{
@Override
public void run()
{
    System.out.println("我在用runable创建线程");
}

public static void main(String[] args)
{
    //创建一个线程任务
    RunableTest task = new RunableTest();
    //将任务交给Thread对象
    Thread thread = new Thread(task);
    thread.start();
}
}
自定义线程池

自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池,用该类很容易实现自定义的线程池,这里先贴上示例程序:

package com.example.demo.thread;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPool{
    public static void main(String[] args){
        //创建等待队列   
        BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20);
        //创建线程池,池中保存的线程数为3,允许的最大线程数为5  
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,50,TimeUnit.MILLISECONDS,bqueue);
        //创建七个任务   
        Runnable t1 = new MyThread();
        Runnable t2 = new MyThread();
        Runnable t3 = new MyThread();
        Runnable t4 = new MyThread();
        Runnable t5 = new MyThread();
        Runnable t6 = new MyThread();
        Runnable t7 = new MyThread();
        //每个任务会在一个线程上执行  
        pool.execute(t1);
        pool.execute(t2);
        pool.execute(t3);
        pool.execute(t4);
        pool.execute(t5);
        pool.execute(t6);
        pool.execute(t7);
        //关闭线程池   
        pool.shutdown();
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "正在执行。。。");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
线程池的参数详解
1. corePollSize:核心线程数。在创建了线程池后,线程中没有任何线程,等到有任务到来时才创建线程去执行任务。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

2.maximumPoolSize:最大线程数。表明线程中最多能够创建的线程数量。

3.keepAliveTime:空闲的线程保留的时间。

4.TimeUnit:空闲线程的保留时间单位。
		TimeUnit.DAYS;               //天
		TimeUnit.HOURS;             //小时
		TimeUnit.MINUTES;           //分钟
		TimeUnit.SECONDS;           //秒
		TimeUnit.MILLISECONDS;      //毫秒
		TimeUnit.MICROSECONDS;      //微妙
		TimeUnit.NANOSECONDS;       //纳秒
5.BlockingQueue<Runnable>:阻塞队列,存储等待执行的任务。参数有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue可选。

6.ThreadFactory:线程工厂,用来创建线程

7.RejectedExecutionHandler:队列已满,而且任务量大于最大线程的异常处理策略。
有以下取值
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

线程的状态管理

1、线程睡眠—sleep:

调用sleep线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。

线程睡眠的原因:线程执行的太快,或需要强制执行到下一个线程。

线程睡眠的方法(两个):sleep(long millis)在指定的毫秒数内让正在执行的线程休眠。

sleep(long millis,int nanos)在指定的毫秒数加指定的纳秒数内让正在执行的线程休眠。

public class SynTest {
    public static void main(String[] args) {
        new Thread(new CountDown(),"倒计时").start();
    }
}

class CountDown implements Runnable{
    int time = 10;
    public void run() {
        while (true) {
            if(time>=0){
                System.out.println(Thread.currentThread().getName() + ":" + time--);
                try {
                	 //睡眠时间为1秒
                    Thread.sleep(1000);                                                   
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

每隔1秒会打印一次

2、线程让步—yield:

该方法和sleep方法类似,也是Thread类提供的一个静态方法,可以让正在执行的线程暂停,但是不会进入阻塞状态,而是直接进入就绪状态。相当于只是将当前线程暂停一下,然后重新进入就绪的线程池中,让线程调度器重新调度一次。也会出现某个线程调用yield方法后暂停,但之后调度器又将其调度出来重新进入到运行状态

package com.example.demo.thread;

public class SynTest
{
public static void main(String[] args)
{
    yieldDemo ms = new yieldDemo();
    Thread t1 = new Thread(ms, "张三吃完还剩");
    Thread t2 = new Thread(ms, "李四吃完还剩");
    Thread t3 = new Thread(ms, "王五吃完还剩");
    t1.start();
    t2.start();
    t3.start();
}
}

class yieldDemo implements Runnable
{
int count = 20;

@Override
public void run()
{
    while (true)
    {
        if (count > 0)
        {
            System.out.println(Thread.currentThread().getName() + count-- + "个瓜");
            if (count % 2 == 0)
            {
                // 线程让步
                Thread.yield();
            }
        }
    }
}
}
sleep和yield的区别:

①sleep方法声明抛出InterruptedException,调用该方法需要捕获该异常。yield没有声明异常,也无需捕获。
②、sleep方法暂停当前线程后,会进入阻塞状态,只有当睡眠时间到了,才会转入就绪状态。而yield方法调用后 ,是直接进入就绪状态。

3.线程合并—join:

当B线程执行到了A线程的.join()方法时,B线程就会等待,等A线程都执行完毕,B线程才会执行。
join可以用来临时加入线程执行。

package com.example.demo.thread;
public class SynTest
{
    public static void main(String[] args) throws InterruptedException {
        yieldDemo ms = new yieldDemo();
        Thread t1 = new Thread(ms, "张三吃完还剩");
        Thread t2 = new Thread(ms, "李四吃完还剩");
        Thread t3 = new Thread(ms, "王五吃完还剩");
        t1.start();
        //线程合并
        t1.join();
        t2.start();
        t3.start();
        System.out.println( "主线程");
    }
}

class yieldDemo implements Runnable
 {
     int count = 20;
    @Override
    public void run()
    {
        while (true)
        {
            if (count > 0)
            {
                System.out.println(Thread.currentThread().getName() + count-- + "个瓜");
                if (count % 2 == 0)
                {
                    // 线程让步
                    Thread.yield();
                }
            }
        }
    }
}

设置优先级

每个线程执行时都有一个优先级的属性,优先级高的线程可以获得较多的执行机会,而优先级低的线程则获得较少的执行机会。与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的也并非没机会执行。

MAX_PRIORITY =10

MIN_PRIORITY =1

NORM_PRIORITY =5


ThreadDemo td = new ThreadDemo();
Thread t1 = new Thread(td,“张三”);
t1.priority(9);            //设置优先级
t1.start();              //设置完毕

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值