Tread

一、构造函数

在这里插入图片描述

1、默认空构造

public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
//默认线程名字Thread-0,静态同步自增
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
    return threadInitNumber++;
}

2、Runnable子类

public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
 
//线程调用start方法时,底层会调用run方法,如果重写则执行runnable实现的方法 
 @Override
 public void run() {
     if (target != null) {
         target.run();
     }
 } 

3、线程名

    public Thread(String name) {
        init(null, null, name, 0);
    }

4、指定线程栈大小

/**  
  *	ThreadGroup 如果没有设置默认是创建该线程的线程的TreadGroup(一般是main)
  * 如果传入一个Runnable子类,就会调用其run方法。如果为null,将调用Tread自己的run();
  * name 设置线程名字,可以不设置,默认从线程Thread-0开始(当然这个构造必须设置)
  * stackSize 代表着该创建的线程占用的栈内存大小,栈内存可以不设置,默认为0,将会忽略;不同操作系统也有影响;线程栈越少可以创建的线程数量越多,越多则线程内方法调用深度越深。
  * */
public Thread(ThreadGroup group, Runnable target, String name,long stackSize)
 {
        init(group, target, name, stackSize);
}
=====================================================
 private void init(ThreadGroup g, Runnable target, String name,
                   long stackSize, AccessControlContext acc,
                   boolean inheritThreadLocals) {
     if (name == null) {//需设置线程名
         throw new NullPointerException("name cannot be null");
     }

     this.name = name;
	//这个父线程就是帮你调用start方法的线程
     Thread parent = currentThread();
     SecurityManager security = System.getSecurityManager();
     if (g == null) {
         /* Determine if it's an applet or not */
         /* If there is a security manager, ask the security manager what to do. */
         if (security != null) {
             g = security.getThreadGroup();
         }

         /* If the security doesn't have a strong opinion of the matter
            use the parent thread group. */
         if (g == null) {//获取创建本线程的groupTread
             g = parent.getThreadGroup();
         }
     }

     /* checkAccess regardless of whether or not threadgroup is
        explicitly passed in. */
     g.checkAccess();

     /*
      * Do we have the required permissions?
      */
     if (security != null) {
         if (isCCLOverridden(getClass())) {
             security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
         }
     }

     g.addUnstarted();

     this.group = g;
     this.daemon = parent.isDaemon();
     this.priority = parent.getPriority();
     if (security == null || isCCLOverridden(parent.getClass()))
         this.contextClassLoader = parent.getContextClassLoader();
     else
         this.contextClassLoader = parent.contextClassLoader;
     this.inheritedAccessControlContext =
             acc != null ? acc : AccessController.getContext();
     this.target = target;
     setPriority(priority);
     if (inheritThreadLocals && parent.inheritableThreadLocals != null)
         this.inheritableThreadLocals =
             ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
     /* Stash the specified stack size in case the VM cares */
     this.stackSize = stackSize;

     /* Set thread ID */
     tid = nextThreadID();
 }

在这里插入图片描述

二、线程组
public class ThreadConstruction {

    public static void main(String[] args) {
        Thread t1 = new Thread("t1");

        ThreadGroup testGroup = new ThreadGroup("testGroup");
        Thread t2 = new Thread(testGroup,"t2");
        ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();
        System.out.println("当前线程的线程组是"+currentGroup);
        System.out.println("t1 的线程组 和当前线程组关系"+ (t1.getThreadGroup() == currentGroup));
        System.out.println("t1 的线程组 == t2 的"+ (t1.getThreadGroup() == t2.getThreadGroup()));
    }
}

当前线程的线程组是java.lang.ThreadGroup[name=main,maxpri=10]
t1 的线程组 和当前线程组关系true
t1 的线程组 == t2 的false

main线程是由jvm创建的默认线程组;

构造线程时没有设置线程组,默认和父线程属于同一个线程组;和父线程的优先级一样。

三、 start方法

源码如下,Thread调用start方法,里面有个本地方法start0,jvm会调用run方法(也称为执行单元)。

public synchronized void start() {
	//线程只能start一次,不然会IllegalThreadStateException异常
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();
四、 守护线程

原理:都属于同一个线程组

特点
1、如果非守护线程结束,则不管守护线程什么状态都将结束(包括守护线程里面子线程);默认为非守护线程。
2、设置tread.setDaemon(true);必须在调用start方法之前调用,不然会报 IllegalThreadStateException异常

作用
1、比如做如下,网路连接心跳,设置心跳线程为守护线程,如果其他线程结束,心跳也没意义存在,所以跟着结束
2、做为后台线程,随着程序关闭或者jvm退出而关闭
3、垃圾回收线程,就是守护线程,不然jvm退出还在垃圾处理。

①外面守护线程,内线程非守护
public class HartDaemo {

    public static void main(String[] args) throws InterruptedException {
        Thread hartThread = new Thread() {
            @Override
            public void run() {
                Thread innerThread = new Thread() {
                    @Override
                    public void run() {
                        while (true){
                            System.out.println("内部线程正常中...........");
                        }
                    }
                };
                innerThread.start();
                while (true){
                    System.out.println("心跳正常中...........");
                }
            }
        };
        //设置为守护线程
        hartThread.setDaemon(true);
        hartThread.start();

        Thread.sleep(1000);
        System.out.println("main线程结束...........");
    }
}

心跳正常中...........
心跳正常中...........
main线程结束...........
内部线程正常中...........
内部线程正常中...........
内部线程正常中...........
Process finished with exit code 0
由于main线程结束,守护线程和内部线程也随着结束    
② 外部守护线程,内部守护
public class HartDaemo {

    public static void main(String[] args) throws InterruptedException {
        Thread hartThread = new Thread() {
            @Override
            public void run() {
                Thread innerThread = new Thread() {
                    @Override
                    public void run() {
                        while (true){
                            System.out.println("内部线程正常中...........");
                        }
                    }
                };

                innerThread.setDaemon(true);
                innerThread.start();
                while (true){
                    System.out.println("心跳正常中...........");
                }
            }
        };
        //设置为守护线程
        hartThread.setDaemon(true);
        hartThread.start();

        Thread.sleep(1000);
        System.out.println("main线程结束...........");
    }
}
心跳正常中...........
心跳正常中...........
main线程结束...........
内部线程正常中...........
内部线程正常中...........
内部线程正常中...........
Process finished with exit code 0
由于main线程结束,守护线程和内部线程也随着结束    
五、优先级和id
public class PriorityDemo {

    public static void main(String[] args) {
        for (int i=1; i<100;i++){
            Thread t1 = new Thread();
            System.out.println("线程一:"+ t1.getId()+".....i="+ i);

            t1.setPriority(Thread.MIN_PRIORITY);//1级最低
            t1.start();
        }

        for (int i=1; i<100;i++){
            Thread t2 = new Thread();
            System.out.println("线程二:"+ t2.getId()+".....i="+ i);

            t2.setPriority(Thread.MAX_PRIORITY);//10级最高
            t2.start();
        }
        for (int i=1; i<100;i++){
            Thread t3= new Thread();
            System.out.println("线程三:"+ t3.getId()+".....i="+ i);

            t3.setPriority(Thread.NORM_PRIORITY); //5级中间,默认优先级
            t3.start();
        }
    }
}
控制台:随便显示几个看看就行了
线程一:97.....i=87
线程一:98.....i=88
线程二:206.....i=97
线程二:207.....i=98
线程二:208.....i=99
线程三:209.....i=1
线程三:210.....i=2

结论:并没有按照优先级执行,所以控制不住;只能说应该高优先级的优先于低优先级的运行
。如果想要实现优先效果,可以自己做一个队列。

源码

public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {        // 优先级控制在 1-10之间,数字越大,优先级越高
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }

线程的优先级只能在1-10之间,如果指定的线程优先级大于线程组的优先级,那么将被设置为线程组的最大优先级

/**
 * 线程优先级,具有不确定性
 */
public class ThreadPriority {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("testGroup");
        group.setMaxPriority(7);
        Thread thread = new Thread(group, "testThread");
        thread.setPriority(10);
        System.out.println("设置后的testThread线程的优先级为"+thread.getPriority());
        System.out.println("默认主线程的优先级为"+Thread.currentThread().getPriority());
    }
}
设置后的testThread线程的优先级为7
默认主线程的优先级为5

线程的优先级默认和它的父线程优先级一致。

六、sleep() 方法

sleep 是一个静态方法,有两个重载方法,一个需要传入毫秒数,另外一个既需要毫秒数也需要纳秒数

public static void sleep(long millis,  int nanos)  throws InterruptedException
public static void sleep(long millis)  throws InterruptedException

sleep方法会使当前线程进入指定毫秒数的休眠,暂停执行,虽然给定一个休眠时间但最终要以系统的定时器和调度器精度为准,需要注意此时线程不会放弃任何的关于“monitor”锁的所有权,每个线程之间进入睡眠互不影响

JDK1.5之后,通过TimeUnit 可以对sleep方法提供很好的封装,使用它可以省去时间单位的换算步骤,如需要线程休眠1个小时10分10秒10毫秒

  TimeUnit.HOURS.sleep(1);
  TimeUnit.MINUTES.sleep(10);
  TimeUnit.SECONDS.sleep(10);
  TimeUnit.MILLISECONDS.sleep(10);
七、yield( ) 方法

yield方法属于一种启发式的方法,提醒调度器我愿意放弃CPU当前执行权,CUP的资源不紧张时候,则会忽略这种提醒
调用yield方法可能使当前线程从Running 状态切换到Runnable 状态,一般这个方法不太常用。

关于yield 和 sleep
  • sleep会导致当前线程暂停指定的时间,没有CPU时间的消耗,到指定时间之后会自动被系统唤醒
  • yield 只是对CPU调度器的一个提示,如果CPU没有忽略这个提示,它会导致线程上下文的切换
  • yield 会是Running 状态下的 Thread 进入 Runnable 状态(如果CPU调度器没有忽略这个提示的话)
  • sleep 会使线程短暂进入block,会在给定的时间内释放CPU资源
  • sleep几乎百分之百完成给定时间的休眠,而yield的提示并不能一定保证
  • A线程sleep,B线程调用A线程的interrupt 会捕获到中断信号,而yield 则不会
六、 join( ) 方法

join 某个线程A,会使当前线程B进入等待,直到线程A结束生命周期,或者到达给定时间,那么在此期间B线程将处于Blocked状态,而不是A线程(放在thread.start();之后)

public final void join() throws InterruptedException
public final synchronized void join(long millis, int nanos)  throws InterruptedException
public final synchronized void join(long millis)  throws InterruptedException 
public class JoinDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 1; i < 100; i++) {
                System.out.println("一号同学:.....上几号i=" + i + "课程");
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 1; i < 100; i++) {
                System.out.println("二号同学:.....上几号i=" + i + "课程");
            }
        });

        t1.start();
        t2.start();
        //main线程阻塞,执行t1线程任务
        t1.join();//t1.join(1000);设置超过1秒就不优先执行完这个了
        t2.join();//等待t1和t2执行完,才执行main线程代码。

		//Thread.currentThread().join(); main线程等待main线程结束,导致最后一直等待
        System.out.println(" 放假。。。");
    }
}
控制台:如果不加人上面两个join方法,导致直接没上完课,学生就放假了,不符合情理
七、interrupt

如下的方法(可中断方法)调用会使当前线程进入阻塞状态,而调用当前线程的interrupt方法,就可以中断阻塞

  • Object 的 wait方法
  • Object 的 wait(long) 方法
  • Object 的 wait() 方法
  • Thread 的 sleep(long) 方法
  • Thread 的 sleep(long, int) 方法
  • Thread 的 join 方法
  • Thread 的 join(long) 方法
  • Thread 的 join(long int) 方法
  • InterruptibleChannel 的 IO 操作
  • Selector 的 wakeup 方法
  • 其他方法
    上述方法都会是当前进程进入阻塞状态,若另外的一个线程调用被阻塞线程的interrupt 方法,则会打断这种阻塞,因此上面这种方法有时也称为可中断方法,记住 : 打断一个线程并不等于改线程的生命周期结束,仅仅是打断了当前线程的阻塞状态。

一旦线程在阻塞的情况下被打断,都会抛出一个InterruptedException 的异常,这个异常相当于一个信号一样通知当前线程被打断了

public class ThreadInterrupt {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                TimeUnit.MINUTES.sleep(10);
            } catch (InterruptedException e) {
                 System.out.println("i am be interrupted");
            }
        });

        thread.start();
        TimeUnit.MILLISECONDS.sleep(2);
        thread.interrupt();
    }
}
通过创建一个线程,并企图睡眠10分钟,但大约在2毫秒之后就被主线程调用interrupt 方法打断,打印 “i am be interrupted”

如果一个线程调用interrupt方法,将被标记中断标志;如果是执行可中断方法被阻塞时,调用interrupt将会清除中断标志。

/**
 * isInterrupted是Thread的成员方法,主要判断当前线程是否被中断,
 * 该方法仅是对interrupt标识的一个判断,并不会影响标识发生任何改变。
 */
public class ThreadIsInterrupted {
    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {
            //这里不使用sleep()是因为它是一个可中断方法,会捕获到中断信号,从而干扰程序运行结果(sleep():false,false)
            while (true) {
            }
        });
        thread.setDaemon(true);
        thread.start();
        TimeUnit.MILLISECONDS.sleep(2);
        System.out.printf("thread is interrupted? %s\n", thread.isInterrupted());
        thread.interrupt();
        System.out.printf("thread is interrupted? %s\n", thread.isInterrupted());


        //sleep()会捕获中断信号,并且擦除interrupt标识,因此程序结果总是false
        //可中断方法捕获到中断信号后,为了不影响当前线程中的其他方法的正常执行,将interrupt标识复位
        System.out.println("============================================");
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    try {
                        TimeUnit.MINUTES.sleep(1);
                    } catch (InterruptedException e) {
                        System.out.printf("Thread is interrupted? %s\n", isInterrupted());//调用这个方法不能用lambda
                    }
                }
            }
        };
        thread1.setDaemon(true);
        thread1.start();

        TimeUnit.MILLISECONDS.sleep(2);
        System.out.printf("Thread is interrupted? %s\n", thread1.isInterrupted());

        thread1.interrupt();
        TimeUnit.MILLISECONDS.sleep(2);
        System.out.printf("Thread is interrupted? %s\n", thread1.isInterrupted());
    }
}
thread is interrupted? false
thread is interrupted? true
============================================
Thread is interrupted? false
Thread is interrupted? false
Thread is interrupted? false

isInterrupted 和 interrupted 区别

  • isInterrupted是 Thread 的一个成员方法,主要判断当前线程是否被中断,该方法仅仅是对interrupt 标识的一个判断,并不会影响标识发生任何改变。
  • interrupted 存在很大差别,需要注意的是可中断的方法捕获到中断信号后,也就是捕获到异常之后会擦除interrupt 标识,相当于标识复位

源码如下:区别就是interrupted会清除中断标志

public static boolean interrupted() {
     return currentThread().isInterrupted(true);
 }

 public boolean isInterrupted() {
     return isInterrupted(false);
 }

对于前面有中断标志没有清除的情况,后面该线程有可中断方法,还将被中断

/**
 * interrupted()是一个静态方法,用于判断当前线程是否被中断,但是和成员方法isInterrupted()还是有很大区别,
 * 调用该方法会直接擦除线程的interrupt标识,需要注意的是,如果当前线程被打断了,第一次调用interrupted会返回true,
 * 并且立即擦除线程的interrupt标识,第二次包括以后的调用永远返回false,除非在此期间线程再一次被打断
 */
public class ThreadInterrupted {
    public static void main(String[] args) throws InterruptedException {
        //如果一个线程在没有执行可中断方法之前就被打断,那么接下来执行可中断方法,会立即被中断。
        //也就是说,如果一个线程设置了interrupt表示,那么接下来的可中断方法会立即被中断
        System.out.println("============================================");
        //判断当前线程是否被中断,这里用成员方法isInterrupted()也不会有影响
        System.out.println("Main thread is interrupted? "+Thread.interrupted());
        //中断当前线程
        Thread.currentThread().interrupt();
        //interrupted擦出了后面就看不到flag标志
        //System.out.println("Main thread interrupted? "+Thread.interrupted());
        //判断当前线程是否被中断,这里用了成员方法isInterrupted(),原因是interrupted()会立即擦除线程的interrupt标识
        //导致程序运行结果不同(下面的sleep不会被打断)
        System.out.println("Main thread isInterrupted? "+Thread.currentThread().isInterrupted());

        //当前线程执行可中断方法
        try {
            TimeUnit.MINUTES.sleep(1);
        }catch (InterruptedException e){
            System.out.println("I will be interrupted still.");
        }
    }
}
============================================
Main thread is interrupted? false
Main thread isInterrupted? true
I will be interrupted still.

参考: 多线程 中断

八、关闭线程方式
/**
 * 示例3 - 线程stop强制性中止,破坏线程安全的示例
 */
public class Demo3 {
  public static void main(String[] args) throws InterruptedException {
    StopThread thread = new StopThread();
    thread.start();
    // 休眠1秒,确保i变量自增成功
    Thread.sleep(1000);
    // 暂停线程
     thread.stop(); // 错误的终止
//   thread.interrupt(); // 正确终止
    while (thread.isAlive()) {
      // 确保线程已经终止
    } // 输出结果
    thread.print();
  }
}

public class StopThread extends Thread {
  private int i = 0, j = 0;

  @Override
  public void run() {
    synchronized (this) {
	    // 增加同步锁,确保线程安全
	    ++i;
	    try {
	      // 休眠10秒,模拟耗时操作
	      Thread.sleep(10000);
	    } catch (InterruptedException e) {
	      e.printStackTrace();
	    }
	    ++j;
    }
  }

  /** * 打印i和j */
  public void print() {
  System.out.println("i=" + i + " j=" + j);
  }
}
结果:i=1 j=0

stop强制终止线程,执行到一半终止有安全问题

由于stop方法存在诸多问题,不建议使用,强制终止线程并清除monitor锁信息,所以我们考虑使用如下方案:
1、使用标记判断进行线程关闭
2、使用中断加守护线程(如下案例)
先设置任务线程为守护线程,利用main线程控制执行线程中断来控制任务线程中断。

public class ThreadService {//线程调度服务,用来控制文件拷贝任务

    private Thread thread; //该线程用来调度资源拷贝线程的
    private boolean flage = false;//拷贝线程结束改变状态为true

    public void execute(Thread task) 
    {//拷贝执行方法,task为拷贝线程
        thread = new Thread(() -> {
            task.setDaemon(true);//设置copy线程为守护线程
            task.start();
            try {
                task.join();//怕copy线程还没启动就随非守护线程结束了
                flage = true;
            } catch (InterruptedException e) {
//                e.printStackTrace();
            }
        });
        thread.start(); //开启调度线程
    }

    public void shutdown(Long mills)
    { //关闭线程方法
        Long logoTime = System.currentTimeMillis();
        while(!flage){//如果没有完成拷贝任务
            if ((System.currentTimeMillis() - logoTime) > mills){ //查看是否超时
                System.out.println("超时。。拷贝任务被强制结束");
                thread.interrupt();//调度线程中断,非守护线程都结束了,守护线程也跟着结束
                break;//跳出循环,main线程和thread线程
            }else {
	                try {
	                    thread.sleep(1);
	                } catch (InterruptedException e) {
	                    System.out.println("执行线程被打断");
	                    e.printStackTrace();
	                }
            }
        }
    }

}
  public static void main(String[] args) {
        Long logoTime = System.currentTimeMillis();
        ThreadService threadService = new ThreadService();
        threadService.execute( new Thread(()->{
            while(true){  //拷贝文件中,时间非常长

            }
     /*       try {//5秒就搞定了拷贝
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
        }));

        threadService.shutdown(10_000l);//10秒
        System.out.println(System.currentTimeMillis() - logoTime);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值