Java线程

线程是操作系统能够进行运算调度的最小单元,每个进程有多个线程组成,每个线程都有一个执行任务即我们编写的一段代码。进程默认启动一个线程来执行main函数,这个线程一般称为主线程。操作系统运算调度指的是CPU的调度,操作系统启动以后需要管理成百上千个线程,而CPU的数量是有限的,所以需要调度这些线程使每个线程都有机会执行。

为什么使用线程

现代计算机的CPU基本都有多个内核,通过Java线程可以充分利用多核CPU的运算能力。比如我们可以创建多个线程,每个线程执行一个死循环,这样就能让CPU的使用率达到100%。还可以利用线程来简化系统设计,如GUI编程中可以使用一个线程来专门处理与用户的交互工作,使用另外一个线程专门进行复杂耗时的运算工作,这样设计使每个线程专注于一件事情,代码容易编写。如果把这两个工作放到一个线程中代码将非常难写,同时还有可能出现卡UI的现象,即未能及时响应用户的操作。

创建线程

下面是创建线程的示例代码:

 

package com.shaoshuidashi;


class Task implements Runnable {
    public void run(){
        while(true){
            try {
                System.out.println("im in Task thread");
                Thread.sleep(1000);
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}
public class Explore {
    public static void main(String[] args){
        Thread trd = new Thread(new Task());
        trd.start();
        while(true){
            try {
                System.out.println("im in main thread");
                Thread.sleep(1000);
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}
/*输出
im in main thread
im in Task thread
im in main thread
im in Task thread
......
 */

我们创建线程是为了执行一些特定的任务,通过实现Runnable接口的run方法,把需要线程执行的任务代码放到run方法中即可,上例中线程的任务是每间隔一秒打印“im in Task thread”。接下来创建Thread对象并把任务对象传递给它,这样线程已经创建好了,紧接着调用Thread对象的start方法启动线程,线程启动以后我们的任务就会被执行。子线程启动后,我们不想主线程退出,所以让主线程每间隔一秒打印“im in main thread”。

线程池

线程的创建和销毁是有一定开销的,频繁创建销毁线程会对应用程序的性能造成影响,对于需要频繁创建销毁线程的场景,我们可以把使用过的线程缓存起来,等下次需要线程时直接从缓存里面取线程,这样就免去了线程的创建和销毁开销。线程缓存的另外一个名字叫做线程池。下面是线程池的使用示例:

 

package com.shaoshuidashi;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Task implements Runnable {
    public void run(){
        while(true){
            try {
                System.out.println(Thread.currentThread());
                Thread.sleep(1000);
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}
public class Explore {
    public static void main(String[] args){
        ExecutorService exec = Executors.newCachedThreadPool();
        exec.execute(new Task());
        exec.execute(new Task());
        exec.execute(new Task());
        while(true){
            try {
                System.out.println(Thread.currentThread());
                Thread.sleep(1000);
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}
/*输出
Thread[main,5,main]
Thread[pool-1-thread-3,5,main]
Thread[pool-1-thread-2,5,main]
Thread[pool-1-thread-1,5,main]
Thread[pool-1-thread-1,5,main]
Thread[pool-1-thread-3,5,main]
Thread[pool-1-thread-2,5,main]
Thread[main,5,main]
......
 */

Java使用ExecutorService来表示线程池,上例使用Executors的静态方法newCachedThreadPool创建了一个线程池,线程池有很多种类型,这里创建的是CachedThreadPool。通过ExecutorService对象的execute方法可以把任务传递给线程池,上例中我们传递了三个任务到线程池,任务每间隔1秒打印线程名字,从输出可以看出每一个任务都有不同的线程执行。

常用的线程池有:

  • CachedThreadPool,动态创建线程,最多可以创建Integer.MAX_VALUE个线程。
  • FixedThreadPool,创建固定数量的线程,数量有使用者指定。
  • SingleThreadExecutor,只创建一个线程,所有任务排队执行。

创建示例如下:

 

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

后台线程

在C/C++中当主线程退出以后整个应用程序也将退出,但是Java和C/C++不同,主线程退出以后只要还有其它线程在执行,整个应用程序就不会退出。有一种方法可以达到主线程退出整个应用就退出的效果,那就是把非主线程设置为后台线程。后台线程的特点是:当所有的非后台线程结束时,程序会终止,同时会杀死进程中的所有非后台进程。

 

package com.shaoshuidashi;

class Task implements Runnable {
    public void run() {
        while(true){
            try {
                System.out.println(Thread.currentThread());
                Thread.sleep(1000);
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }
}
public class Explore {
    public static void main(String[] args) throws InterruptedException{
        Thread trd = new Thread(new Task());
        trd.setDaemon(true);
        trd.start();
        Thread.sleep(10*1000);
    }
}

调用线程对象的setDaemon方法可以设置线程为后台线程,上例程序运行10s后将退出。如果把代码trd.setDaemon(true)注释,应用程序会一直运行不会退出。

最后

多线程不仅可以充分利用CPU资源,合理的使用线程还可以使软件设计更简洁,线程的创建和销毁有一定的开销,使用线程池可以避免这些开销。

在实际开发中,线程之间往往需要共享数据,多个线程直接读写共享数据会产生很多意想不到的错误,所以需要对共享的数据进行保护,下篇文章将学习怎么在多个线程间共享数据。

 

【水煮Java】
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值