程序员半栈工程师

java中Thread的深入了解(一)

2017-09-05  本文已影响85人  Charon_Pluto

线程

        线程是进程中的一个单一顺序的执行单元,也被称为轻量进程(lightweight process)。线程有自己的必须资源,同时与其他线程进程共享全部资源。同一个进程中,线程是并发执行的。由于线程的之间的相互制约,线程有就绪、阻塞、运行、结束等状态。

一 新建线程

我们先创建两个线程类:

新建一个MyThread类 继承 Thread来覆盖 run方法:

MyThread类

新建MyRunnable类 实现Runnable 接口:

MyRunable类

创建以及执行两个线程类

主线程Main 线程结果

二 验证线程并发

在上述创建的MyThread类和MyRunnable类各自的run方法输出上 加个for循环for(int i=0;i<10000;i++);

循环线程 输出结果extends

如果是顺序执行,应该会是 extendsThread全部输出完 ,才输出implement thread。但是这并不是而是出现交叉结果 证明线程是并发执行。

交叉原理就是每个代码 在cpu时间片内执行一段时间,到时间就换一个代码 在cpu时间片内执行,但是如果时间片内打印完了,就看不见交叉现象。

单核cpu

三 线程声明周期(或者线程状态)

java中线程基本上有以下图片中的几种状态:

线程状态

首先我们要做一个输出线程状态的类:

线程状态

public static void printThreadState(Thread thread){

Thread.State state=thread.getState();

switch(state){

//1.新建状态

caseNEW:

info(thread.getName()+" state is NEW");break;

//2.执行状态

case RUNNABLE:

info(thread.getName()+" state is RUNNABLE");break;

//3.等待超时状态

case TIMED_WAITING:

console.info(thread.getName()+" state is TIMED_WAITING");break;

//4.等待状态

case WAITING:

info(thread.getName()+" state is WAITING");break;

//5.阻塞状态

case BLOCKED:

info(thread.getName()+" state is BLOCKED");break;

//6.结束状态

caseTERMINATED:

info(thread.getName()+" state is TERMINATED");break;

default:

info

(thread.getName()+" state is UNKOWN");

}

}

//打印

public static  void info(Object o){

System.out.println(String.valueOf(o));

}

1.新建状态:

new之后 start之前都属于NEW状态,一旦状态变成其他,则不会再变成NEW状态。

public static  void main(String args[]) throws InterruptedException{

Thread t=new Thread(new Runnable() {

@Override

public void run() {

}

});

printThreadState(t);//查看new之后的thread状态

}

2.执行状态

在run()方法里的时候,线程处于执行状态。一旦run()方法执行完毕,则状态就不是执行状态了

执行状态 线程状态输出结果

3.等待超时状态

当调用wait(long timeout)方法的时候,线程处于等待状态,并且超时后并不马上返回,而是等待获取锁后返回。

查看wait(long timeout) 输出结果

验证超时后并不马上返回,而是等待获取锁后返回。

添加一条输出结果 如果是wait(long timeout)方法时候,提前先把锁获取过来等待20s 会发现输出结果线程还是阻塞状态

所以我们会发现一个问题,wait的超时只是一个预想, 多少时间后我再去获取锁,如果拿到就返回 拿不到就处于等待状态。这里就会发现这里等待是最少需要long timeout时间。

wait(等多少时间后 再去竞争锁),wait的时间内 我不去竞争 跟别人抢锁,所以这个时间 不保证锁一定能抢回来。

注意:Thread.sleep 也会进入TIME_WAITING状态。

5.等待状态

wait()睡眠不参与抢锁,等待notify来进行唤醒。

放弃抢锁,处于waiting状态 waiting状态时候唤醒 输出结果

wait 也可以通过interrupt唤醒,interrupt是中断线程的方法。

5.阻塞状态

如果获取不到锁,就一直在阻塞状态,直到获取锁为止。

把循环锁死,当状态位锁死的时候释放锁 输出结果

6.结束状态

在run()方法执行结束以后,线程处于结束状态

结束状态

但是当这个线程执行结束了之后,是否代表这个线程被销毁了呢。然而只是线程执行单元销毁了,但是线程对象还在。虽然这个线程对象还存在,但是它已经不能进行再次的start()方法进行执行了。

四 研究多线程锁问题

经典并发问题:当运行一万个线程想List插入的时候,预期结果是有10000个,但是输出确不是10000个,而是少于1000个。原因是 线程并发执行获取list的长度是过期的。

比如我拿到list ,你也拿到list ,咱俩同时看list里有1条数据 我把数据插入第二个位置 ,你也插入第二个位置 ,就会导致list最终结果只有2条数据,实际上插入了3条数据。

public static void ThreadMuliteTest() throws InterruptedException {

final List list=new ArrayList();

for(int i=0;i<10000;i++){

new Thread(new Runnable() {

@Override

public void run() {

list.add(Thread.currentThread().getName());

}

}).start();

}

Thread.sleep(1000);

System.out.println(list.size());

}

输出结果

这就是线程并发问题,并发问题有个解决办法 就是对线程进行枷锁让他排队,我拿到了 你就不能拿。

synchronized 代码段是java给线程的一个控制权,可以锁住一个资源,防止被其他人用。加锁之后线程就进入了 排队状态 ,只有一个线程能拿到list的权限 其他等待。

还有一种操作 就是我得到资源后 ,可以释放一段时间给别人用 ,超时了 我在拿回来自己用。

public static void ThreadGetWait() throws InterruptedException {

Object lock=new Object();

new Thread(new Runnable() {

@Override

public void run() {

synchronized (lock){

console.info("线程一:拿到锁");

try {

lock.wait(100);//释放100ms

} catch (InterruptedException e) {

e.printStackTrace();

}

console.info("线程一:锁又被我拿回来了");

try {

Thread.sleep(1000);//睡1s 锁不给别人

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}).start();

Thread.sleep(10);//休眠10ms防止第二个线程拿到锁

new Thread(new Runnable() {

@Override

public void run() {

synchronized (lock){

console.info("线程二:拿到锁");

try {

Thread.sleep(2000);//拿到锁后我要休眠2s

} catch (InterruptedException e) {

e.printStackTrace();

}

console.info("线程二:锁又回来了?");

}

}

}).start();

}

输出结果

因为线程1先运行 肯定拿到锁,线程一暂时释放。所以线程二紧接着拿到了锁,线程二休眠了2s 期间并没有释放锁,所以线程1一直处于阻塞状态,线程二执行完成后 释放锁 线程一 打印 最后一句。

上一篇下一篇

猜你喜欢

热点阅读