一、一个线程的生命周期
图片说明
阻塞状态(Blocked):线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态

二、几类线程的概念
主线程:JVM调用main产生
当前线程:Thread.currentThread()获取到的
守护线程 aka 后台线程,isDaemon()判断、setDaemon()设置。为其他线程提供服务,不依赖主线程结束而结束。

三、线程安全 aka 为啥需要做多线程开发
多个线程访问同一个共享资源时,可能会因为抢占cpu时间片而造成问题

四、线程锁的三种方法
示例:卖票程序

public class TestDemo{
    static int count = 100;
    static int sale =0;
    static Runnable r = new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(count>0) {
                System.out.println(Thread.currentThread().getName()+"--");
                count--;
                sale++;
                if(count<=0) {
                    System.out.println("售罄");
                    return;
                }
                System.out.println("已售:"+sale+",剩余:"+count);
            }
        }
    };
    public static void main(String args[]) {
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        Thread t3 = new Thread(r);
        t1.start();
        t2.start();
        t3.start();
    }
}

图片说明
打印出来是错乱的结果,不是顺序连着的票

所以需要锁机制,让现成的访问有先来后到,前一个正在取值的线程未结束,锁不会得到释放,后面的线程也无法访问

加入synchronize关键字(最重要的一种同步锁):
法一:同步代码块 static Object obj =new Object();

public class TestDemo{
    static int count = 100;
    static int sale =0;
    static Object obj =new Object();
    static Runnable r = new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(count>0) {
                System.out.println(Thread.currentThread().getName()+"--");

                synchronized (obj) {
                    count--;
                    sale++;
                    if(count<=0) {
                        System.out.println("售罄");
                        return;
                }


                System.out.println("已售:"+sale+",剩余:"+count);
                }
//                if(sale%10==0) {
//                    System.out.println("已售:"+sale+",剩余:"+count);
//                }
            }
        }
    };
    public static void main(String args[]) {
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        Thread t3 = new Thread(r);
        t1.start();
        t2.start();
        t3.start();
    }
}

输出顺序正常
PS:也可以使用static byte[] obj = new byte[0];代替static Object obj =new Object();
因为零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
但是在synchronize外声明byte或Object对象都需要用static关键字,因为静态变量才可以独立与对象存在,否则无法调用。

法二:同步类 Math.class
上述代码中,synchronize(obj)也可以改为synchronize(Math.class),即将同步代码块改为同步类锁,都是synchronize实现的方法

public class TestDemo{
    static int count = 100;
    static int sale =0;
    //static Object obj =new Object();
    static Runnable r = new Runnable() {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(count>0) {
                System.out.println(Thread.currentThread().getName()+"--");

                synchronized (Math.class) {
                    count--;
                    sale++;
                    if(count<=0) {
                        System.out.println("售罄");
                        return;
                }        
                System.out.println("已售:"+sale+",剩余:"+count);
                }
            }
        }
    };
    public static void main(String args[]) {
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        Thread t3 = new Thread(r);
        t1.start();
        t2.start();
        t3.start();
    }
}

法三:同步方法

直接定义方法不加锁:

public class TestDemo{
    static int count = 10;
    static int sale =0;
    //static Object obj =new Object();
    static Runnable r = new Runnable() {     
        @Override
        public void run() {
            // TODO Auto-generated method stub
            while(count>0) {
                saleFunc();
                }
        }
         public void saleFunc() {
             if(count<=0) {
                    System.out.println("售罄");
                    return;
              }
               System.out.println(Thread.currentThread().getName()+"--");  
               count--;
               sale++;
               System.out.println("已售:"+sale+",剩余:"+count);
               if(count<=0) {
                    System.out.println("售罄");
                    return;
              }
         }
    };
    public static void main(String args[]) {
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        Thread t3 = new Thread(r);
        t1.start();
        t2.start();
        t3.start();
    }
}

图片说明
会造成打印错乱

同步方法:直接在方法定义时加上synchronize关键字即可
图片说明

五、死锁
在多线程开发中会遇到,一般出现在高负载场景下
what:因争抢资源而陷入僵局,互相等待无法执行下去

why:
当前线程拥有其他线程需要的资源
当前线程等待其他线程已拥有的资源
都不放弃自己拥有的资源

how:
1.线程之间交错执行 解决:以固定的顺序加锁
2.执行某方法时就需要持有锁,且不释放 解决:缩减同步代码块范围,最好仅操作共享变量时才加锁
3.永久等待 解决:使用tryLock()定时锁,超过时限则返回错误信息
4.JDK提供了两种方式在编码阶段来检测死锁:
JconsoleJDK自带的图形化界面工具,使用JDK给我们的的工具JConsole
Jstack是JDK自带的命令行工具,主要用于线程Dump分析。