SetPriority:

  在java中线程的优先级是从1-10,默认值为5,优先级越高,抢占到CPU的概率越高。

SetDaemon:

  设置为守护线程,当其他非守护线程执行完毕之后,守护线程会陆续结束(代码可能不会执行完毕)

yield:

  让出CPU的执行权,但是可能连续随机执行到这个线程

join:

  把指定线程插入到当前线程之前,在指定线程执行完毕之后继续执行当前线程



线程的生命周期(一)

在sleep结束之后,会进入就绪状态而不是直接运行下面的代码



同步代码块

  把操作共享数据的代码锁起来

  锁默认打开,有一个线程进去了,锁自动关闭

  里面的代码全部执行完毕,线程出来,锁自动打开

synchronized(锁对象){
	代码块
}

需要传入一个锁对象,这个锁对象必须的唯一的,可以为static创建的对象(具体是什么锁)

当需要循环时,需要放在循环内部,确保代码能被执行完毕

一般会把当前类的字节码对象传入锁对象(MyThread.class)

同步方法

  用synchronized修饰的方法

同步方法锁对象不能自己指定,如果是非静态方法,则锁对象为this,如果是静态方法,则锁对象是当前类的字节码文件对象

  技巧:可以先创建同步代码块,在抽取成方法。



StringBuilder和StringBuffer的区别:

  前者是线程不安全的,后者是线程安全的,所有的方法都有synchronized修饰。



Lock锁

在我们使用锁的时候,上锁和释放锁都是自动的,所以在JDK5之后提供了一个新的锁对象Lock

我们可以利用Lock接口的实现类对象ReentrantLock来自己上锁和释放锁

Lock lock = new ReentrantLock();

可以加上static修饰,来保证多个对象共享同一个锁

一定要保证单个线程在执行完毕后释放锁资源,不然会导致其他线程无法结束!

解决方案:上锁后的所有代码放到try中,解锁方法放在finally中


!死锁


死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

  假设有两把锁,A和B,线程1先上A锁再上B锁,线程2先上B锁再上A锁,那么在执行过程中,就有可能A锁被线程1占有,B锁被线程2占有,进入死锁状态。

  如何避免:在写代码的过程中避免两个锁的嵌套!



生产拿着和消费者(等待唤醒机制)


生产者消费者模式是一个十分经典的多线程协作的模式

  两个线程交替运行,其中一条线程作为生产者来生产数据,另一条线程作为消费者来消费数据。

  光是这两者无法保证线程交替运行,需要引入第三者来控制线程的执行。

  当数据未准备就绪时,消费线程处于Wait状态,直到被生产线程唤醒notify

    消费者:判断桌子上有无食物,如果没有就等待

    生产者:判断桌子上是否有食物,有则等待,否则制作食物,把食物放到桌子上,叫醒等待的消费者开吃。

  常见方法:

    void wait() 当前线程等待,直到被其他线程唤醒

    void notify() 随机唤醒单个线程

    void notifyAll() 唤醒所有线程

代码实现:

测试类

public class ThreadDemo {  
    public static void main(String[] args) {  
        /*  
        * 需求: 完成生产者和消费者(等待唤醒机制)的相关代码        *             实现线程轮流交替执行的效果        * */  
        //创建线程对象  
        Cook c = new Cook();  
        Foodie f = new Foodie();  
  
        //设置名字  
        c.setName("厨师");  
        f.setName("吃货");  
  
        //开启线程  
        c.start();  
        f.start();  
    }  
}

厨师类

public class Cook extends Thread {  
    @Override  
    public void run() {  
        /*  
         * 1.循环         * 2.同步代码块         * 3.判断:到了末尾         * 4.判断:没到末尾-核心逻辑         * */        while (true) {  
            synchronized (Desk.lock) {  
                if (Desk.count == 0) {  
                    break;  
                } else {  
                    if (Desk.foodFlag == 1) {  
                        try {  
                            Desk.lock.wait();  
                        } catch (InterruptedException e) {  
                            throw new RuntimeException(e);  
                        }  
                    } else {  
                        //做面  
                        System.out.println("厨师正在做面条");  
                        //修改食物状态  
                        Desk.foodFlag = 1;  
                        //唤醒消费者  
                        Desk.lock.notifyAll();  
                    }  
                }  
            }  
        }  
    }  
}

吃货类

public class Foodie extends Thread {  
    @Override  
    public void run() {  
        /*  
         * 1.循环         * 2.同步代码块         * 3.判断:到了末尾         * 4.判断:没到末尾-核心逻辑         * */  
        while (true) {  
            synchronized (Desk.lock) {  
                if (Desk.count == 0) {  
                    break;  
                } else {  
                    //先判断桌子上是否有面条  
                    if (Desk.foodFlag == 0) {  
                        try {  
                            //如果没有就等待  
                            Desk.lock.wait();//底层逻辑:让当前线程和锁进行绑定  
                        } catch (InterruptedException e) {  
                            throw new RuntimeException(e);  
                        }  
                    } else {  
                        //修改桌子的状态  
                        Desk.count--;  
                        Desk.foodFlag = 0;  
                        //开吃  
                        System.out.println("正在吃面条,还能吃" + (Desk.count) + "碗!");  
                        //吃完唤醒厨师继续  
                        Desk.lock.notifyAll();//唤醒这把锁绑定的所有线程  
                    }  
  
                }  
            }  
        }  
    }  
}

桌子类

public class Desk {  
    /*  
    * 作用:    * 控制生产者和消费者的执行    *    * */  
    // 桌子上是否有面条,0/1。  
    // 如果使用boolean类型,只能控制两条线程。    // 为了通用性,定义为int类型。    public static int foodFlag = 0;  
  
    //总个数  
    public static int count = 10;  
  
    //锁对象  
    public static final Object lock = new Object();  
}

等待唤醒机制实现方式二(阻塞队列方式实现)

Java阻塞队列

类似于厨师与吃货中间的管道,做好的面条都放到管道中,我们可以规定这个管道的长度(放多少碗),这个管道就是阻塞队列。

什么是阻塞:

  厨师put数据时。放不进去,会等着。

  吃货take数据时。取出第一个数据,如果取不到也会等待。

代码实现

测试类

public class BlockQueueDemo {  
    /*  
    *    * 需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码    *     细节:    *            生产者和消费者必须使用同一个阻塞队列    * */    public static void main(String[] args) {  
        //1.在测试类中创建阻塞队列  
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);  
        //2.创建厨师和吃货的对象  
        CookForBQD c = new CookForBQD(queue);  
        FoodieForBQD f = new FoodieForBQD(queue);  
        //3.命名  
        c.setName("厨师");  
        f.setName("吃货");  
        //4.开始  
        c.start();  
        f.start();  
        /*  
        * 在阻塞队列put和take方法的底层都实现了lock锁        * 因为打印语句在锁的外面,所以会出现连续打印的现象,但是不会对共享数据造成影响。        * */    }  
}

厨师类

public class CookForBQD extends Thread{  
  
    ArrayBlockingQueue<String> queue;  
    //为了使用同一个阻塞队列,在创建时传递阻塞队列的对象  
  
    public CookForBQD(ArrayBlockingQueue<String> queue) {  
        this.queue = queue;  
    }  
  
    @Override  
    public void run() {  
        while(true){  
            //不断把面条放入队列  
            try {  
                queue.put("面条");  
                System.out.println("厨师放了一碗面条");  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
        }  
    }  
}

吃货类

public class FoodieForBQD extends Thread{  
  
    ArrayBlockingQueue<String> queue;  
    //为了使用同一个阻塞队列,在创建时传递阻塞队列的对象  
  
    public FoodieForBQD(ArrayBlockingQueue<String> queue) {  
        this.queue = queue;  
    }  
  
    @Override  
    public void run() {  
        while(true){  
            //不断吃面条  
            try {  
                queue.take();  
                System.out.println("吃货吃了一碗面条");  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }  
        }  
  
    }  
}

线程的六种状态(生命周期)

在java中没有定义运行状态,只是一种方便称呼的叫法,所谓运行状态就是CPU在执行线程。


java.lang.Thread.State枚举类列举了线程的六种状态

Alt

NEW

使用new Thread()创建一个线程,并且没有调用该线程的start()方法,此时该线程处于NEW状态

RUNNABLE

调用start()方法后,该线程进入RUNNABLE状态

RUNNABLE对应操作系统的Running和Ready两种状态,分别表示正在运行和等待分配CPU资源

BLOCKED

运行到synchronized代码块但未获得对应的锁,线程进入BLOCKED阻塞状态

获得对应的锁后重新进入Runnable状态

WAITING

等待状态,一般用于等待其他线程执行特定的操作

线程调用了没有参数的Object.wait()Thread.join()方法(其实Thread.join()方法就是调用了Object.wait()方法)、LockSupport.park()方法会进入这个状态

TIMED_WAITING

超时等待状态,线程等待一个时间的流逝,经过这段时间后,该线程由系统唤醒,也可以提前唤醒。

线程调用带参数的Object.wait()Thread.join()Thread.sleep()方法、LockSupport.parkNanos()LockSupport.parkUntil()方法进入该状态

TERMINATED

线程结束

run()执行完毕或遇到了没有捕获异常