多线程之Thread

2020-04-03  本文已影响0人  无证程序员不猖狂

什么是Thread


操作系统能够调度和运算的最小单元,运行于进程之内。

Thread的优和劣


目前的电脑都是多核的,单一任务在执行的时候如果其他cpu空闲对于电脑来讲就是一种资源的浪费。thread的存在就是为了让空闲cpu能够最大化被利用,既然cpu使用了那么针对电脑的各种资源就会被调动。

优总结起来就是:资源利用率更好,程序响应更快;

伴随的互联网及移动互联网的发展,大家对于程序的性能似乎更注重了,但是对于多线程的使用貌似都使用的不是很好,毕竟cpu的切换,线程的死锁,共享资源时更慢等,并发场景更能说明这些问题(不信你看Doug Lea 不也是在1.5的时候才完成了并发包嘛?)。

整体来说就是:大家都使用不好thread。。。。

Thread 的使用


class PrimeThread extends Thread {

*        long minPrime;

*        PrimeThread(long minPrime) {

*            this.minPrime = minPrime;

*        }

*

*        public void run() {

*            // compute primes larger than minPrime

*             . . .

*        }

*    }

class PrimeRun implements Runnable {

*        long minPrime;

*        PrimeRun(long minPrime) {

*            this.minPrime = minPrime;

*        }

*

*        public void run() {

*            // compute primes larger than minPrime

*             . . .

*        }

*    }    

*    PrimeRun p = new PrimeRun(143);

*    new Thread(p).start()


以上代码是Thread 源码注释中的经典实现。thread当中还是很多实例方法,例如 setPrority、setName、setDaemon、setContextClassLoader 接下来仔细分析一下

setPrority :设置限制被执行的优先级,优先级越高就越被cpu优先执行,每一个线程都有一个默认的优先级继承自父线程

/**

* The default priority that is assigned to a thread.

*/

public static final int NORM_PRIORITY =5;

setName :为当前线程指定一个名字,如果不设置默认则是“Thread-”+nextThreadNum() 这里的 nextThreadNum()执行逻辑就是threadInitNumber++,threadInitNumber是个静态对象记录了当前app之内的所有thread总数

setDaemon:设置线程为守护线程,既然称之为守护那么就要出现被守护者,被守护者就是普通的用户线程,有一点要明白如果被守护的用户线程没有退出那么守护线程是不会退出的

setContextClassLoader:简单点说就是为当前线程设置一个classloader (类加载器),然而这个动作的最根本原因是为了打破java固有的类加载机制-----双亲委托加载机制支持java spi这种设计,这一点接下来会做一个详细的解释和说明。

我们可以写一段demo,

Thread thread =new Thread(){

@Override

    public void run() {

System.out.println(this.getName());

}

};

System.out.println(thread.getContextClassLoader());

thread.start();

输出了AppClassLoader,在程序总并没有设置它,其实在Launcher源码中,创建SystemClassLoader的时候主动调用了Thread.setContextClassLoader();

publicLauncher() {

try{

extClassLoader=ExtClassLoader.getExtClassLoader();

}catch(IOExceptioniOException) {

thrownewInternalError("Could not create extension class loader", iOException);

    }

try{

this.loader=AppClassLoader.getAppClassLoader(extClassLoader);

}catch(IOExceptioniOException) {

thrownewInternalError("Could not create application class loader", iOException);

    }

Thread.currentThread().setContextClassLoader(this.loader);

Stringstr=System.getProperty("java.security.manager");

所以在程序中我们能够看到getContextClassLoader是我们的应用类加载器(appclassloader或者叫systemclassloader).

spi(service provider interface)设计的典型场景:Class.forName("com.mysql.jdbc.Driver") java 只做了Driver的接口声明,并没有做实现,所有的实现都交给了众多的数据库厂商 如:

mysql(com.mysql.jdbc.Driver) 

oracle(oracle.jdbc.driver.OracleDriver") 

sqlserver(com.microsoft.jdbc.sqlserver.SQLServer-Driver)

但是在应用跑起来后,加载Driver接口的classloader 是 启动类加载器(bootclassloader ),而加载Driver实现(mysql oracle 他们的实现)的classloader是应用类加载器(systemclassloader),根据双亲委托知道父加载器加载的类或接口可以被子类访问到,子加载器加载的类或者接口父加载器无法访问和知晓,这样的话在当前应用程序的lib下设置的jar 如果没有Thread.currentThread().getContextClassLoader()则我们目前的这种结构就不能实现,所以Thread setContextClassLoader getContextClassLoader 最终就解决了父类不能访问子类或者无关系loader的访问文件的问题,这一点有点拗口需要好好理解。

通过上面的描述和demo可以看到,目前线程的执行逻辑和工作单元都在Thread或者对应的Runnable内进行实现。

下一个文章我们继续学习Thread,不过更多的是学习另外的一种形式。

上一篇下一篇

猜你喜欢

热点阅读