通过JVM给我们的入口,可以知道运行期间有几个线程:

ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
for (ThreadInfo threadInfo : threadInfos){
    System.out.println(threadInfo);
}


一、启动线程的三种方式:
1.继承 Thread
2.实现Runnable接口
3.实现Callable接口(有返回值)
public class NewThread {

    private static class UseRun implements Runnable{

        @Override
        public void run() {
            System.out.println("I am implements Runnable");
        }

    }

    private static class UseCall implements Callable<String>{

        @Override
        public String call() throws Exception {
            System.out.println("I am implements Callable");
            return "CallResult";
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        UseRun useRun = new UseRun();
        //Runnable接口能够直接作为Thread的构造参数
        Thread thread1 = new Thread(useRun);
        UseCall useCall = new UseCall();
        //Callable接口不能作为Thread的构造参数
//需要通过FutureTask类的转换,将其转换为Runnable接口
        //因为FutureTask实现了Runnable接口
        FutureTask<String> futureTask = new FutureTask<>(useCall);
        Thread thread2 = new Thread(futureTask);
        thread2.start();
//通过futureTask的get方法得到返回值
     //get方法是一个阻塞方法,一直等到有返回值后才会继续往下走
        System.out.println(futureTask.get());
    }

}
二、停止线程的三种方式:
1.自然执行完成死亡
2.在没有对异常进行处理时抛出异常会中止线程
3.早期stop()等因为过于强势,无法保证线程资源释放,容易导致死锁的问题‘
4.interrupt():中断一个线程,将中断标志位置为true
  isinterrupted():判定当前线程是否处于中断状态
  static方法interrupted():判定当前线程是否处于中断状态,同时将中断标志位改为False,这是2和3的最主要的区别
由于Java中线程是协作式的,interrupt方法只会通知线程应该中断了,并不会强制中断
目的:为了让每个线程有时间释放占有的资源
在实际操作过程中是要自己轮询标志位进行判断的,如果不对标志位进行判断,则不会有任何影响。

问:但是interrupt()和isinterrupted方法都是Thread类里面的方法,Runnable接口怎么办?
  判断isinterrupted的时候用Thread.currentThread.isinterrupted()方法就可以了
package thread;
/*
    如何安全地中断线程
 */
public class EndThread {

    private static class UseThread extends Thread{
        public UseThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while (!isInterrupted()){
                System.out.println(threadName);
            }
            System.out.println(isInterrupted()+"中断标志位");
        }
    }

    private static class UseThread2 extends Thread{
        public UseThread2(String name) {
            super(name);
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while (!Thread.currentThread().isInterrupted()){
                System.out.println(threadName);
            }
            System.out.println(Thread.currentThread().isInterrupted()+"中断标志位");
        }
    }


    private static class UseThread3 extends Thread{
        public UseThread3(String name) {
            super(name);
        }

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while (!isInterrupted()){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    //这里打印的是false
                    System.out.println(isInterrupted()+"中断标志位");
            //自行interrupt一次才会中断
            interrupt();
                    e.printStackTrace();
                }
                //此时线程并没有中断
                System.out.println("***");
            }
            System.out.println(isInterrupted()+"中断标志位while结束后");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread endThread = new UseThread3("endThread");
        endThread.start();
        Thread.sleep(10);
        endThread.interrupt();
    }

}

注意:当方法抛出InterruptedException异常的时候,线程的中断标志位会复位成False,只有在catch语句中再执行一次interrupt方法
  (再次中断)才会中断。InterruptedException所有阻塞方法都会抛出异常
如果说用自己定义的标志位,阻塞时的判断就会不及时,不建议使用

 

java 多线程 连远程服务 设置超时时间_ide

 

 yield():用的少,示意线程让出时间片,然后和其他线程一起抢,可能再次抢到。

 

三、其他知识:
·设置线程优先级1-10:Thread.setPriority(x);
·守护线程:
  一般来说主线程结束后如果子线程还没处理完会继续运行
  守护线程和主线程共死,如GC线程
  特别注意:
    Thread.setDaemon(true);//一定要在start()方法之前设置
    try//finally语句块中的语句在守护线程里面不一定会执行
四、线程间的共享:
·synchronized关键字(内置锁):
  确保任一时刻只能由一个线程处于同步方法或同步块中
  锁的实际上是具体的一个对象。改变对象的标志位指向某一线程。
  对象锁:在普通方法上加,或者synchronized(this){}也是对象锁
  类锁  :在static方法上加,或者synchronized(xxx.class){}(锁的是class对象)
·volatile关键字:
  每次获取值的时候都要从主内存中读取
  每次修改完之后都要把值刷回主内存
  保证了可见性,轻量同步机制,不能保证原子性()。
  最大的用处是在一个线程写,多个线程读时最常用

·ThreadLocal线程变量:
  确保每个线程只使用自己的那一个拷贝,主要用在连接池,保持每个线程的连接,内部用Map维护
package thread;

public class UseThreadLocal {
    //例化了一个ThreadLocal对象。我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。 
      虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用ThreadLocal的 
      set()方法设置的值。即使是两个不同的线程在同一个ThreadLocal对象上设置了不同的值,他们仍然无法访问到对方的值。
static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
        //设置初始值
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };


    //运行三个线程
    public void StartThreadArray(){
        Thread[] runs = new Thread[3];
        for (int i = 0; i < runs.length; i++) {
            runs[i] = new Thread(new TestThread(i));
        }
        for (int i = 0; i < runs.length; i++) {
            runs[i].start();
        }
    }

    //测试线程,将ThreadLocal变量的值变化,并写回,看看线程之间是否会相互影响
    public static class TestThread implements Runnable{

        int id;

        public TestThread(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"start");
            Integer s = threadLocal.get();
            s = s+id;
            threadLocal.set(s);
            System.out.println(Thread.currentThread().getName()+"当前"+threadLocal.get());
        }
    }

    public static void main(String[] args) {
        UseThreadLocal test = new UseThreadLocal();
        test.StartThreadArray();
    }

}
五、线程间协作
·等待和通知的标准范式
  wait()、notify/notifyall()是Object中的方法。
  等待方:
    1、获取对象的锁
    2、循环里判断条件是否满足,不满足调用wait方法
    3、条件满足执行业务逻辑
  通知方:
    1、获取对象的锁
    2、改变条件
    3、通知所有等待该对象的线程
  尽量使用notifyall,防止发生信号丢失情况
  例子:Express类
package thread.waitNotify;

public class Express {

    public final static String CITY = "shanghai";
    private int km;//里程数
    private String site;//快递到达地点

    public Express() {
    }

    public Express(int km, String site) {
         = km;
        this.site = site;
    }

    //变化地点,然后通知处于wait状态并需要处理地点的线程进行业务处理
    public synchronized void changeSite(){
        this.site="beijing";
        notifyAll();
    }

    //变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理
    public synchronized void changeKm(){
         = 101;
        notifyAll();
    }

    public synchronized void waitKm(){
        while (<=100){
            try {
                wait();
                System.out.println("check km thread"+
                        Thread.currentThread().getId()+
                        "is notified");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("the km is"+);
    }

    public synchronized void waitSite(){
        while (this.site.equals(CITY)){
            try {
                wait();
                System.out.println("check site thread"+
                        Thread.currentThread().getId()+
                        "is notified");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("the site is"+this.site);
    }
}
测试代码:
package thread.waitNotify;

import java.util.concurrent.TimeUnit;

public class TestWN {
    private static Express express = new Express(0,Express.CITY);

    //检查里程数的线程,不满足条件就一直等待
    private static class CheckKm extends Thread{
        @Override
        public void run() {
            express.waitKm();
        }
    }

    //检查地点的线程,不满足条件就一直等待
    private static class CheckSite extends Thread{
        @Override
        public void run() {
            express.waitSite();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new CheckKm());
        Thread thread2 = new Thread(new CheckSite());
        thread1.start();
        thread2.start();
        TimeUnit.MILLISECONDS.sleep(100);
        express.changeKm();
    }
}
·等待超时模式(连接池的实现):
  假设等待时间为T,当前时间now+T以后超时
  long overTime = now+T;
  long remain = T;//等待的持续时间
  while(result 不满足条件 && remain>0){
    wait(remain);
    remain = overTime-now;//剩下的持续时间
  }
  return result;
数据库连接池模拟:
  //在mills时间内拿不到数据库连接,返回一个null
  public Connection fetchConn(long mills){
    synchronized(poolList){
      long overTime = System.currentTime+mills;
      long remian = mills;
      while(poolList.isEmpty && remian>0){
        poolList.wait(remain);
        remian = overtime - now();
      }
      Connection result = null;
      if(!poolList.isEmpty()){
        result = poolList.removeFirst();
      }
      return result;
    }
  }
//放回数据库连接
public void releaseConn{
  synchronized(poolList){
    poolList.addLast(conn);
    poolList.notifyAll();
  }
}


·join()方法:
  面试点:线程A执行了线程B的join方法,那么线程A就必须要等待B执行完成了之后才能继续执行(请美女排你前面插队)


六、调用yield、sleep、wait、notify等方法对锁有何影响
线程在执行yield以后,持有的锁是不是放的,cpu仍可能调度这个线程
sleep方法也不会释放锁
在调用wait方法之前必须要持有锁,在调用wait方法之后这个锁就会被释放,当wait方法返回的时候线程会重新持有锁
notify在调用之前也必须要持有锁,在同步代码块跑完之后才会释放锁