关于线程的那些事
什么是线程,java的代码被编译成字节码存放在内存中,它要被执行就要被cpu调用,而线程就cpu执行在内存中进程中的一条任务线,main方法本身就是一条线程,这就是为什么它能被运行,比如为什么你能边打游戏,边听队友讲话的原因,用最简洁概括就是线程是进程中能够被独立执行的子任务,或为是操作系统能够进行调度最小单位
上面谈到进程,所谓进程就是程序被执行的状态就是进程,它可以包含多条并行的线程,每条线程执行不同任务,线程的种类有内核线程(内核线程是直接由内核本身启动的进程,内核就是cpu)和用户线程(就是在简单点内存中,也就是我们平时写的普通线程),同一进程中线程共享该进程的全部资源,同一进程的多条线程有自己的调度栈,自己的寄存器环境,自己的线程本地存储
由线程引发的问题如下(任何理论的东西,最终转化为现实解决问题的能力,你的价值取决于你能解决什么问题,这点是面试必备的,找工作是你能解决什么工作问题,而不是让工作解决你的问题,无论哪个面试官都一样)
使用多线程
线程的实现方式有两种一种是实现runable接口,一种就是继承thread类,但是有个问题是java中是属于单继承,多实现接口,
下面就使用thread来进行
class Powder extends Thread{
public Powder(){
System.out.println("Thread.currentTread().getName="+Thread.currentThread().getName());
System.out.println("this.getName="+this.getName());
}
@Override
public void run() {
System.out.println("Thread.currentTread().getName="+Thread.currentThread().getName());
System.out.println("this.getName="+this.getName());
for (int i=0;i<=200;i++){
System.out.println("傻逼------------"+i);
if (i==20){
this.interrupt();
}
}
System.out.println(this.isAlive()+"--------kkk");
}
}
第二种就是实现runnable接口,并将该实现的对象引用传进thread构造方法中
class RunnableOne implements Runnable{
@Override
public void run() {
System.out.println("hello world");
}
}
public static void main(String[] args) throws InterruptedException {
/*Powder powder=new Powder();
System.out.println(powder.isAlive());
powder.start();*/
RunnableOne runnableOne=new RunnableOne();
Thread thread=new Thread(runnableOne);
thread.start();
}
启动线程
上面两种方式对于java这种单继承来说最好使用接口模式,然后再将其传进thread中,然后调用strat方法开启动
首先弄清楚进行程序的次数,如果是单次的话完全可以用匿名内部类,其次就是多次用到的话,还有别的线程,这时就需要用thread中的构造方法来实现
需要特别注意的一点就是你要将实现多线的具体执行的代码写到run中,如果thread直调用run的话就是串行,thread中的start方法是通知线程管理器,这个是属于操作系统的,表示去通知系统的线程管理器(线程管理器有本地操作系统的一个管理线程的服务去管理)等待器安排时间调用run方法来执行,所以线程就具有偶发性
线程的start的顺序并不代表线程启动顺序
在自定义多线程类中的实例变量有共享和不共享之分
当变量定义在run方法中的话,使用该变量是进行运算是安全的
但是如果定义在成员变量中时,如果实现该线程多个对象,就可以对该成员变量进行操作,如果按照常规的话,strat方法提交给线程管理器,线程管理器自行安排就会出现乱枪现象,如
class OneMain extends Thread{
private int count=40;
@Override
public void run() {
super.run();
count--;
System.out.println("由"+this.currentThread().getName()+"计算count="+count);
}
}
public static void main(String[] args) {
OneMain oneMain=new OneMain();
Thread a=new Thread(oneMain,"a");
Thread b=new Thread(oneMain,"b");
Thread c=new Thread(oneMain,"c");
Thread d=new Thread(oneMain,"d");
a.start();
b.start();
c.start();
d.start();
}
}
结果如下
由c计算count=37
由b计算count=38
由a计算count=39
由d计算count=36
出现这种结果就是一堆向线程管理器请求执行的结果
如何解决这种情况呢就是让他们乖乖排队,就是使用synchronized加到方法上或者是形成synchronized{}块
synchronized public void run() {
super.run();
count--;
System.out.println("由"+this.currentThread().getName()+"计算count="+count);
}
下面为执行结果
由a计算count=39
由d计算count=38
由c计算count=37
由b计算count=36
线程的状态
初始化,就是new出来的线程,但没有调用start方法
运行状态,就是调用start或者是run方法
阻塞,表示线程阻塞于锁
等待(waiting):就是该线程状态等待其他线程做出一些特定动作
超时等待:它在指定时间后自动返回
终止:表示该线程已经执行完毕
如何判断代码被哪条线程执行
就是利用currentThread()方法,例如获取线程名
public void run() {
System.out.println("Thread.currentTread().getName="+Thread.currentThread().getName());
System.out.println("this.getName="+this.getName());
for (int i=0;i<=200;i++){
System.out.println("hello------------"+i);
if (i==20){
this.interrupt();
}
}
如何知道那些线程活着
isAlive()方法,使用对象调用判断是否线程活着,用要判断的线程的引用
所谓活着的状态指的是已经提交给线程管理器准备开启或处于执行状态
如何让线程休眠
sleep(X)方法,就是指定毫秒数内让当前“处于活动状态的线程”休眠
如何获得线程的id
getId()方法获取线程的唯一标识
如何停止线程
stop()方法虽然可以做到但最好别用后期会被删除,用interrupt()方法,这个方法是标记要停
如何判断线程是否处于停止状态
interrupted(),和interrupted(),测试线程是否已经中断
interrupled()具有清理线程的状态,isterrupted()具有判断状态但不具备清理功能
能停止线程–异常法
可以在线程中用for语句来判断线程是否停止状态,如果停止就不再运行
在沉睡中停止
就是让线程先沉睡然后main线程执行interrupted方法,线程中的catch会抓捕住这个错误
使用return停止线程
以上停止线程的方法都不对
暂停线程
可以使用suspend暂停,也可以使用resume方法来使用恢复