从零开始的高并发之路(一)

  1. 多线程
  2. 创建一个多线程的三种方式
  3. 线程状态
  4. 策略模式在线程类中的应用
  5. 守护线程
  6. 线程优先级
  7. thread.join()
  8. 中断
  9. 结束线程
  10. CountDownLatch

多线程

创建一个多线程的三种方式

public class Testthread {
    
    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();//第一种 继承thread
        Thread threadB = new Thread(new ThreadB());//第二种 实现 Runnable
        threadA.start();
        threadB.start();

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是一个线程"+ Thread.currentThread().getName());
            }
        }); //第三种  匿名类
        threadC.start();

    }
   static  class ThreadA extends  Thread {
        @Override
        public void run() {
            System.out.println("这是一个线程"+ Thread.currentThread().getName());
        }
    }
    static  class ThreadB implements   Runnable {
        @Override
        public void run() {
            System.out.println("这是一个线程"+ Thread.currentThread().getName());
        }
    }
}

线程状态

线程状态大致分为新建状态、就绪状态、运行状态、阻塞状态及死亡状态。 他们之间的联系见下图:

threadstatus

  • 新建状态 new
      新建状态既创建一个线程的实例,此时还未开始运行。
  • 就绪状态 runable
      线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权
  • 运行状态 running
      可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序run()方法,直白点就是等待cpu调度的状态。
  • 阻塞状态 blocked
      阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行 以下几种方式会导致阻塞:
      1.正在运行的线程通过调用sleep()方法进入等待阻塞状态,JVM会把该线程放入等待队列(waitting queue)中。
      2.正在运行线程在获取对象的同步锁时,若该同步锁被别的线程占用,进入同步阻塞状态,则JVM会把该线程放入锁池(lock pool)中。
      3.正在运行的线程产生了I/O请求,JVM会把线程调整为阻塞状态,等待处理完成,再次进入可运行状态。
  • 死亡状态 termate
      线程正常完成退出或发生异常,线程死亡。

策略模式在线程类中的应用

策略模式 本质:分离算法,选择实现 ,我们提供一个具体的策略角色AbstractClassA,它是对具体方法的抽象 ,可以由接口或抽象类完成 我们称他为抽象策略角色。 包含该策略必备的属性和方法。ClassB 和ClassC分别是策略的两种不同的策略执行角色,我们称之为具体策略角色,是对策略的实现。ClassD 是用来操作策略的角色,其职责本来是隔离客户端与策略类的耦合,让客户端完全与上下文环境沟通,无需关系具体策略,其被称为上下文角色,ClassD 内部一定会有一个策略类的一个成员变量(AbstractClassA),当我们需要实现具体的策略时,可以向ClassD中传入具体的策略的实现类,执行具体的策略。


public abstract class AbstractClassA {
    /*
    * 获取一个名字
    * */
    public abstract  String getname(String str);
}

class ClassB extends  AbstractClassA{

    @Override
    public String getname(String str) {
        return str;
    }
}

class ClassC extends  AbstractClassA{

    @Override
    public String getname(String str) {
        return "hello world";
    }
}

 final  class  ClassD {
   private AbstractClassA a;

    ClassD(){
    }
    ClassD(AbstractClassA a){
        this.a=a;
    }

    String getname(String str)
    {
        if(a!=null)
            return a.getname(str);
        return null;
    }

}
class ClassE{

    public static void main(String[] args) {
        String name = "xiaoming";
        ClassD D = new ClassD();
        /*此时是未使用任何抽象类的 直接调用的我们ClassD的getname方法 */
        System.out.println(D.getname(name));
        ClassC c = new ClassC();
        /*我们给ClassD中传入一个AbstractClassA 我们就可以自由的对AbstractClassA的getname方法进行实现*/
        ClassD D1 = new ClassD(c);
        System.out.println(D1.getname(name));
        ClassB b = new ClassB();
        ClassD D2 = new ClassD(b);
        System.out.println(D2.getname(name));
    }

}


> 回过头我们再看thread类,thread中包含一个Runnable接口的抽象策略角色,而我们具体去实现Runnable的run方法时,我们就自己去实现了一个具体策略角色,thread类被定义为上下文类。其run方法通过构造方法传入具体的策略角色进行实现run方法。

``` java
@FunctionalInterface
public interface Runnable {
    
    public abstract void run();
}

public
class Thread implements Runnable {

     /* What will be run. */
    private Runnable target;

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
    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;

        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) {
                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 synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        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();//调用c++ 的start0  c++再执行java的run方法
            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();


     @Override
    public void run() {
        if (target != null) {
            target.run(); //执行策略类的run方法
        }
    }
}

我们看到当我们实例化一个thread时,无论是那个构造函数最终都会调用private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) 这个方法,其中有个stacksize表示创建线程所使用的栈的大小,如果为传入的话,默认会由jni的c++ 自动初始化,我们通常不会使用这个参数,如果一定要使用,会通过直接设置java 的栈参数指定 -Xss<size> 指定java线程的栈大小。

守护线程

守护线程会随着主线程的消亡而就是当进程不存在或主线程停止,守护线程也会被停止。

public class Testthread {
    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();//第一种 继承thread
        threadA.setDaemon(true);//设定为守护线程 必须在start之前调用
        threadA.start();
        
    }
   static  class ThreadA extends  Thread {
        @Override
        public void run() {
            System.out.println("这是一个线程"+ Thread.currentThread().getName());
        }
    }
}

线程优先级

线程优先级通过设置一个 thread.setPriority(int newpriority)使该线程具有较高的优先级,cpu会优先分配资源给该线程。(不代表一定会优先执行) 范围是0-10 默认是5.


public class Testthread {
    public static void main(String[] args) throws Exception {
        ThreadA threadA = new ThreadA();
         Thread threadB = new Thread(new ThreadB(),"thread-B");
        threadA.setName("thread-A");
        threadA.setPriority(1);
        threadA.start();
        
        //threadA.setDaemon(true);
        threadB.setPriority(3);
        threadB.start();
        

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是一个线程"+ Thread.currentThread().getName());
            }
        },"thread-C");
        threadC.setPriority(2);
        threadC.start();
        //threadB.join();
        //threadC.join();
        System.out.println(java.lang.Thread.currentThread().getName());

    }
   static  class ThreadA extends  Thread {
        @Override
        public void run() {
            System.out.println("这是一个线程"+ Thread.currentThread().getName());
        }
    }
    static  class ThreadB implements   Runnable {
        @Override
        public void run() {
            System.out.println("这是一个线程"+ Thread.currentThread().getName());
        }
    }
}

/**
 *执行结果
 *main
 *这是一个线程thread-A
 *这是一个线程thread-C
 *这是一个线程thread-B
 */

thread.join()

join方法的作用是父线程等待所有join的线程都执行完成后,才能继续用下运行 有join() ,join(long),join(long,int) 三种 。

  • join() 等待所加入的线程完全执行完成。
  • join(long) 等待所加入的线程一段时间(毫秒) ,如果还未完成,父线程继续执行。
  • join(long,int)同上 精度更高 例如join(1000,10) 1000毫米10纳秒

public class Testthread {
    public static void main(String[] args) throws Exception {
        ThreadA threadA = new ThreadA();
         Thread threadB = new Thread(new ThreadB(),"thread-B");
        threadA.start();
        //threadA.setDaemon(true);
        threadB.start();

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("这是一个线程"+ Thread.currentThread().getName());
            }
        });
        threadC.start();
        threadB.join(); 
        threadC.join();//主线程main会等待线程B和线程C执行完成再执行
        System.out.println(java.lang.Thread.currentThread().getName());

    }
   static  class ThreadA extends  Thread {
        @Override
        public void run() {
            System.out.println("这是一个线程"+ Thread.currentThread().getName());
        }
    }
    static  class ThreadB implements   Runnable {
        @Override
        public void run() {
            System.out.println("这是一个线程"+ Thread.currentThread().getName());
        }
    }
}
--没有join
main
这是一个线程Thread-0
这是一个线程Thread-1
这是一个线程Thread-2

--有join
这是一个线程Thread-0
这是一个线程Thread-1
这是一个线程Thread-2
main

*NOTE: Thread.currentThread().join() 可以用来阻塞自己,线程一直在等自己死亡执行结束。


中断

中断在方法在线程中包含5个方法,interrupt(),isInterrupted(boolean),isInterrupted(),interrupt0()和interrupted()。他在中断 sleep wait join方法时会抛InterruptedException异常。

  • interrupt()设置为中断状态,不能真的中断线程。
  • interrupted() 当前线程设置中断状态
  • isInterrupted(boolean)可以修改中断状态 私有方法
  • isInterrupted() 返回中断状态

private native void interrupt0(); //私有方法

public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    } 
public boolean isInterrupted() {
        return isInterrupted(false);
    }
public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

private native boolean isInterrupted(boolean ClearInterrupted);//私有方法

结束线程

我们知道在线程中,stop()方法存在安全问题,suspend()会发生死锁, 那么我们应该如何正确的结束一个线程呢,首先, 我们可以想到 ,守护线程会随着父线程的消亡而消亡,那么就可以把要执行的程序设置为某一父线程的守护线程,我们想办法让他的父线程停止,守护线程就随之停止了,然后,当我们中断一个线程时,被中断的线程在调用 wait sleep join 等方法时会抛出InterruptedException异常,终止当前线程,我们可以利用这个原理,中断守护线程的父线程。达到停止我们要执行的任务的目的。


public class Threadinterrupt {


    public static void main(String[] args) {
        Threadservice threadservice = new Threadservice();
        threadservice.exec(()->{
            while (true){
                System.out.println("正在执行线程"+Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        try {
            Thread.sleep(5000);
            threadservice.stop();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Threadservice{

    private Thread thread;

       void  exec(Runnable runnable)
       {
           if(thread==null)
           thread = new Thread(()->{
               Thread threadinner = new Thread(runnable);
               /*设定为守护线程*/
               threadinner.setDaemon(true);
               threadinner.start();
           });
           thread.start();
           try {
               thread.join();
           } catch (InterruptedException e) {
               e.printStackTrace();
               return;
           }
       }

       void stop()
       {
           /*调用*/
           if(thread!=null&& thread.isAlive())
           thread.interrupt();
           System.out.println("中断守护线程的父线程");
       }

}

CountDownLatch

CountDownLatch是一个同步工具类 可以理解为线程同步计数器

public class countdownlatchdemo{

CountDownLatch c = new CountDownLatch(10);
    for (int i=0; i<9; i++) {
                new Thread(()->{
                c.countDown()
                 System.out.println(Thread.currentThread().getThreadID);
                }).start();
    }

c.await()

}