Java中的多线程

进程与线程

进程是系统资源分配的最小单位,进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵,通常一个任务就是一个程序,而一个程序就是一个进程。

线程是CPU执行调度的最小单位(资源调度的最小单位),线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。

为什么使用多线程
  • 从计算机底层来说: 线程可以比作是轻量级的进程,是程序执行的最小单位,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。

  • 从当代互联网发展趋势来说: 现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。

多线程特点
  • 线程共享相同的地址空间
  • 线程是轻量级的
  • 线程间通信的成本低
线程的生命周期(线程的状态)

在这里插入图片描述

1)New,新建状态

你创建了Thread类的对象,在调用startK()方法之前则线程处于新建状态

2)Runnable,可执行状态

线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待线程调度程序的调度

3)Running,执行状态

如果线程调度程序选择了该线程,cpu执行代码,则该线程处于运行状态。

4)Non-Runnable(Blocked),阻塞状态

阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5)Dead,死亡状态

线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程的创建
Thread类的几个常用构造函数
    Thread()
	Thread(String name)
	Thread(Runnable r)
	Thread(Runnable r,String name)

1)继承Thread类(任务和线程合并在一起了)

class T extends Thread {
    @Override
    public void run() {
        log.info("我是继承Thread的任务");
    }
}

2)实现Runnable接口(任务和线程分开了)

class R implements Runnable {
    @Override
    public void run() {
        log.info("我是实现Runnable的任务");
    }
}

3)实现Callable接口(利用FutureTask执行任务,有返回值)

class C implements Callable<String> {

    @Override
    public String call() throws Exception {
        log.info("我是实现Callable的任务");
        return "success";
    }
线程的启动
// 启动继承Thread类的任务
new T().start();

//  启动实现Runnable接口的任务
new Thread(new R()).start();

// 启动实现了Callable接口的任务 结合FutureTask 可以获取线程执行的结果
FutureTask<String> target = new FutureTask<>(new C());
new Thread(target).start();
log.info(target.get());
多线程常用方法

1)线程的优先级

  • 每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

  • Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

  • 默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

  • 具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台

//设置线程的优先级
public final void setPriority(int newPriority) 
//获取线程的优先级
public final int getPriority() 

2)线程休眠,sleep()

Thread类的sleep()方法用于在指定的时间内休眠一个线程。

public static void sleep(long miliseconds)throws InterruptedException
public static void sleep(long miliseconds, int nanos)throws InterruptedException
public class ToSleepThread implements Runnable {

    private int ticketNums = 5;
    @Override
    public void run() {
        while (true){
            if(ticketNums <= 0){
                break;
            }
            //模拟网络延迟
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+ " 拿到了第" + ticketNums-- + "张票");
        }
    }
    public static void main(String[] args){
        ToSleepThread sleepThread = new ToSleepThread();
        new Thread(sleepThread,"jack").start();
        new Thread(sleepThread,"lucy").start();
    }
}

output:

jack 拿到了第5张票
lucy 拿到了第4张票
jack 拿到了第3张票
lucy 拿到了第3张票
lucy 拿到了第2张票
jack 拿到了第2张票
jack 拿到了第1张票
lucy 拿到了第1张票
public class ToSleepThread2 {
    public static void main(String[] args){
        turnDown();
//            printCurrentTime();
    }
    //模拟倒计时
    public static void turnDown(){
        int num = 10;
        while (true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(num--);
            if(num <= 0){
                break;
            }
        }
    }
    //输出当前时间
    public static void printCurrentTime(){
        while (true){
            System.out.println(new Date().toString());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3)线程并入,join()

join()是Thread类的一个方法, t.join()方法阻塞调用此方法的线程,直到线程t完成,此线程再继续。如控制A, B, C线程的执行顺序,主线程等待子线程执行结束,都可以用join来实现。

public void join()throws InterruptedException
public void join(long milliseconds)throws InterruptedException
//测试join方法,想象为插队
public class ToJoinThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程vip来了" + i);
        }
    }
    public static void main(String[] args) throws InterruptedException {
        //启动我们的线程
        ToJoinThread joinThread = new ToJoinThread();
        Thread thread = new Thread(joinThread);
        thread.start();
        //主线程
        for (int i = 0; i < 5; i++) {
            if(i == 3){
                try {
                    // 线程插队后,会先把插队的线程执行完成的
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
            System.out.println("主线程运行 ----" + i);
        }
    }
}

output:

主线程运行 ----0
主线程运行 ----1
主线程运行 ----2
线程vip来了0
线程vip来了1
线程vip来了2
主线程运行 ----3
主线程运行 ----4

4)线程礼让,yield()

yield()做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

public static void yield()
public class ToYieldThread {
    public static void main(String[] args){
        MyYield myYield = new MyYield();
        new Thread(myYield,"线程一").start();
        new Thread(myYield,"线程二").start();

    }
}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程开始");
        Thread.yield(); //礼让
        System.out.println(Thread.currentThread().getName() + "线程停止");
    }
}
守护线程

java中的守护线程是为用户线程提供服务的服务提供线程。它的生命取决于用户线程,即当所有用户线程死亡时,JVM自动终止这个线程.

java中有许多守护线程自动运行,例如gc,finalizer等

特点:

  • 它为后台支持任务的用户线程提供服务。它在生活中除了服务于用户线程之外没有其他角色。
  • 它的生命取决于用户线程。
  • 它是一个低优先级线程
//设置当前线程为守护线程或用户线程
public void setDaemon(boolean status)
//判断线程是否为守护线程
public boolean isDaemon()
//测试守护进程
//上帝守护你
public class DaemonTest {
    public static void main(String[] args){
        God god = new God();
        Thread threadGod = new Thread(god);
        //线程默认为用户线程,setDaemon 可以将它改为守护进程
        threadGod.setDaemon(true);
        //开启守护进程
        threadGod.start();

        Thread threadYou = new Thread(new You());
        //开启用户进程
        threadYou.start();
    }
}
//上帝  守护进程
class God implements Runnable{
    @Override
    public void run() {
        //此线程是死循环线程,但设置为守护线程后,当所有的用户线程都死亡时,它会被jvm自动终止。
        while(true){
            System.out.println("god is blessing you");
        }
    }
}
//你 , 用户进程
class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("一直幸福地活着");
        }
        System.out.println("---------goodbye world");
    }
}

tips: 如果想要设置一个用户线程为守护线程,一定要在该线程开启(start方法调用)前设置,否则会抛出IllegalThreadStateException异常。

线程池

Java线程池表示一组等待任务并多次重用的工作线程。对于线程池,可以创建一组固定大小的线程。从线程池中取出一个线程,并由服务提供者分配一个任务。任务完成后,线程再次包含在线程池中。简单来说,它就是一个管理线程的池子。

优点:

  • 它帮我们管理线程,避免增加创建线程和销毁线程的资源损耗
  • 提高响应速度。 如果任务到达了,从线程池拿线程,相对于重新去创建一条线程执行,速度更快。
  • 重复利用。 线程用完,再放回池子,可以达到重复利用的效果,节省资源。
  • 解耦作用。线程的创建于执行完全分开,方便维护。
class WorkerThread implements Runnable {  
    private String message;  
    public WorkerThread(String s){  
        this.message=s;  
    }  
     public void run() {  
        System.out.println(Thread.currentThread().getName()+" (Start) message = "+message);  
        processmessage();//call processmessage method that sleeps the thread for 2 seconds  
    }  
    private void processmessage() {  
        try {  Thread.sleep(2000);  } catch (InterruptedException e) { e.printStackTrace(); }  
    }  
}  

public class TestThreadPool {  
     public static void main(String[] args) {  
        ExecutorService executor = Executors.newFixedThreadPool(3);//creating a pool of 3 threads  
        for (int i = 0; i < 7; i++) {  
            Runnable worker = new WorkerThread("" + i);  
            executor.execute(worker);//calling execute method of ExecutorService  
          }  
        executor.shutdown();  
        while (!executor.isTerminated()) {   }  
  
        System.out.println("Finished all threads");  
    }  
 }  

output:

pool-1-thread-2 (Start) message = 1
pool-1-thread-1 (Start) message = 0
pool-1-thread-3 (Start) message = 2
<!--两秒后-->
pool-1-thread-2 (Start) message = 3
pool-1-thread-3 (Start) message = 4
pool-1-thread-1 (Start) message = 5
<!--两秒后-->  
pool-1-thread-2 (Start) message = 6
<!--两秒后--> 
Finished all threads
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值