线程基础

线程停止

//建议线程正常停止,利用次数,不建议死循环
//建议使用标志位
//不要使用stop或destory等jdk不建议使用的方法
public class MyStop implements Runnable{
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run..." + i++);
        }
    }

    //设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        MyStop myStop = new MyStop();
        new Thread(myStop).start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main" + i);
            if(i == 900){
                //转换标志位停止线程
                myStop.stop();
            }
        }
    }
}

线程休眠sleep

  • sleep(时间)指定当前线程阻塞时间(毫秒)
  • sleep存在异常InterruptedException
  • sleep结束后线程进入就绪态
  • 可以模拟网络延时和倒计时
  • 每一个对象都有一个锁,sleep不会释放锁
import java.text.SimpleDateFormat;
import java.util.Date;

public class MySleep {

    public static void main(String[] args) {
        //倒计时
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //打印当前时间
        Date starttime = new Date(System.currentTimeMillis());
        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(starttime));
                starttime = new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void tenDown() throws InterruptedException{
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num<=0){
                break;
            }
        }
    }
}

线程礼让yield

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行态转为就绪态
  • 让cpu调度,礼让不一定成功
public class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "start");
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "stop");
    }

    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield, "a").start();
        new Thread(myYield, "b").start();
    }
    /* a礼让成功的情况
    astart
    bstart
    astop
    bstop
        不用yield的情况
    astart
    astop
    bstart
    bstop
     */
}

线程强制执行join

源码解析

三种构造函数

void join():当前线程等该加入该线程后面,等待该线程终止。
void join(long millis):当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度。
void join(long millis,int nanos):等待该线程终止的时间最长为 millis 毫秒 + nanos纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度。

源码

// 当前线程等待该线程终止的时间最长为 millis 毫秒。 
public final synchronized void join(final long millis)
throws InterruptedException {
    if (millis > 0) {
        // 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度。
        if (isAlive()) {
            final long startTime = System.nanoTime();
            long delay = millis;
            do {
                wait(delay);
            } while (isAlive() && (delay = millis -
                    TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
        }
    } else if (millis == 0) {
        // join()实际调用的就是join(0)
        // 如果子线程一直存活,就等到子线程执行结束
        while (isAlive()) {
            wait(0);
        }
    } else {
        throw new IllegalArgumentException("timeout value is negative");
    }
}
  • 当main主线程调用threadA.join()时,main线程会获得threadA线程对象锁,只有这样才能执行synchronized join()方法内,调用threadA线程对象的wait()。目的是让持有这个threadA线程对象锁的线程都进入等待,等待threadA线程执行完毕。然后JVM底层lock.notify_all(thread),唤醒持有threadA对象锁的所有线程。

使用

public class MyJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("vip comeing" + i);
        }
    }

    public static void main(String[] args) throws InterruptedException{
        MyJoin myJoin = new MyJoin();
        Thread thread = new Thread(myJoin);
        thread.start();

        for (int i = 0; i < 500; i++) {
            if(i==200){
                thread.join();//插队
            }
            System.out.println("main" + i);
        }
    }
}

状态

  • 初始状态(NEW)

实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。

  • 就绪状态(READY)

    • 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
    • 调用线程的start()方法,此线程进入就绪状态。
    • 当前线程sleep()方法结束、其他线程join()结束、等待用户输入完毕、某个线程拿到对象锁,这些线程也将进入就绪状态。
    • 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
    • 锁池里的线程拿到对象锁后,进入就绪状态。
  • 运行中状态(RUNNING)

线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一的一种方式。

  • 阻塞状态(BLOCKED)

阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

  • 等待(WAITING)

处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

  • 超时等待(TIMED_WAITING)

处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

  • 终止状态(TERMINATED)
    • 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
    • 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

20181120173640764

public class MyState {

    public static void main(String[] args) throws InterruptedException{
        Thread thread = new Thread(()->{
            for (int i = 0; i < 2; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("hhhhhhh");
        });

        //观察状态
        Thread.State state = thread.getState();
        System.out.println(state);//new

        thread.start();
        state = thread.getState();
        System.out.println(state);//run

        while (state != Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
        }
        
//        thread.start();死亡的线程无法再次启动
    }
    
    /*
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
hhhhhhh
TERMINATED
     */
}

线程优先级

  • 范围1~10
  • Thread.MIN_PRIORITY = 1
  • Thread.NORMAL_PRIORITY = 5
  • Thread.MAX_PRIORITY = 10
  • getPriority(),setPriority(int xxx)
import java.util.SortedMap;

public class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "  " + Thread.currentThread().getPriority());
    }
}

class TestPriority{
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + "  " + Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();

        Thread thread1 = new Thread(myPriority);
        Thread thread2 = new Thread(myPriority);
        Thread thread3 = new Thread(myPriority);
        Thread thread4 = new Thread(myPriority);
        Thread thread5 = new Thread(myPriority);

        //先设置优先级再启动
        thread1.start();

        thread2.setPriority(1);
        thread2.start();

        thread3.setPriority(4);
        thread3.start();

        thread4.setPriority(10);
        thread4.start();

        thread5.setPriority(111);
        thread5.start();


    }
}

守护线程daemon

  • jvm必须确保用户线程执行完毕,不需等待守护线程执行完毕。
  • 所有非守护线程都执行完毕后,虚拟机退出。
  • 守护线程不能持有任何需要关闭的资源,例如打开文件等,因为虚拟机退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失。
public class MyDaemon {

    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);// 改为守护线程

        thread.start();// 守护线程开始

        new Thread(you).start();
    }
}

class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 365; i++) {
            System.out.println("开心的活着");
        }
        System.out.println("bye world");
    }
}

class God implements Runnable{

    @Override
    public void run() {
        while (true){
            System.out.println("God bless you");
        }
    }
}

线程同步

  • 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。

  • 一个对象对应一个同步队列。

  • 线程等待时间到了或被notify/notifyAll唤醒后,会进入同步队列竞争锁,如果获得锁,进入RUNNABLE状态,否则进入BLOCKED状态等待获取锁。

  • 20180701221233161

  • Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。

  • Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。

  • thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。

  • obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。

  • obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的(根据JDK版本不同,在等待队列中唤醒的线程在队列里面的位置不同 )。notifyAll()唤醒在此对象监视器上等待的所有线程。

  • LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。

    • 并发:多个线程同时操作同一对象

    • 形成条件:队列+锁🔒

    • 锁机制

      • 一个线程持有锁会导致其他所有需要此锁的线程挂起
      • 加锁释放锁会导致较多的上下文切换和调度延时
      • 如果一个优先级高的线程在等一个优先级低的线程释放锁,会导致优先级倒置
    • 线程不安全的例子

public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();

        new Thread(station, "haha").start();
        new Thread(station, "xixi").start();
        new Thread(station, "hehe").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNums = 10;
    boolean flag = true;//外部停止方式

    @Override
    public void run() {
        while (flag){
            buy();
        }
    }

    private void buy(){
        if(ticketNums <= 0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
    }
}

public class UnsafeBank {
    public static void main(String[] args) {

        Account account = new Account(100,"基金");
        Drawing boy = new Drawing(account, 50, "boy");
        Drawing girl = new Drawing(account, 100, "girl");

        boy.start();
        girl.start();
    }
}

class Account{
    int money;
    String name;

    //alt+insert
    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

class Drawing extends Thread{
    Account account;
    int drawingMoney;
    int nowMoney;

    public Drawing(Account account, int drawingMoney, String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        if(account.money - drawingMoney < 0){
            System.out.println(Thread.currentThread().getName() + "钱不够取不了");
            return;
        }

        //放大问题的发生性
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        account.money -= drawingMoney;
        nowMoney += drawingMoney;

        System.out.println(account.name + "账户余额:" + account.money);
        System.out.println(this.getName() + "手里的钱:" + nowMoney);
    }
    /*
基金账户余额:-50
基金账户余额:50
girl手里的钱:100
boy手里的钱:50
     */
}

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
    //结果不到10000
}

源码

// jdk1.8
public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

// jdk11
public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}
// 借助临时变量s定位并赋值,然后通过size = s + 1给size赋新值
private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

待完善。。。

同步方法

  • synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法必须获得调用该方法的对象的锁才能执行,否则线程就会阻塞,方法一旦执行就会独占该锁
  • 缺陷:若将一个大的方法申明为synchronized会影响效率
  • 方法里需要修改的资源才需要锁
  • synchronized默认锁的是this
class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket station = new BuyTicket();
        new Thread(station, "A").start();
        new Thread(station, "B").start();
        new Thread(station, "C").start();
    }
}

class BuyTicket implements Runnable{
    private int ticketNumbs = 10;
    boolean flag = true;//外部停止方式
    @Override
    public void run() {
        while (flag){
            buy();
        }
    }

    //synchronized 同步方法,锁的是this
    private synchronized void buy(){
        if(ticketNumbs <= 0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNumbs-- + "张票");
    }
/*    A拿到了第10张票
            A拿到了第9张票
    A拿到了第8张票
            A拿到了第7张票
    A拿到了第6张票
            A拿到了第5张票
    C拿到了第4张票
            B拿到了第3张票
    B拿到了第2张票
            B拿到了第1张票*/
}
  • 同步块:synchronized(Obj){}
  • Obj称之为同步监视器
    • 可以是任何对象,推荐使用共享资源作为同步监视器
    • 监视的对象是需要增删改的对象,及会变化的对象
    • 同步方法中不需要指定同步监视器,同步方法中的同步监视器就是this
class UnsafeBank {
    public static void main(String[] args) {

        Account account = new Account(100,"基金");
        Drawing boy = new Drawing(account, 50, "boy");
        Drawing girl = new Drawing(account, 100, "girl");

        boy.start();
        girl.start();
    }
}

class Account{
    int money;
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

class Drawing extends Thread{
    private Account account;
    int drawingMoney;
    int nowMoney;

    public Drawing(Account account, int drawingMoney, String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {

        synchronized (account){
            if(account.money - drawingMoney < 0){
                System.out.println(Thread.currentThread().getName() + "钱不够取不了");
                return;
            }

            // 放大问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            account.money -= drawingMoney;
            nowMoney += drawingMoney;

            System.out.println(account.name + "账户余额:" + account.money);
            System.out.println(this.getName() + "手里的钱:" + nowMoney);
        }
    }
/*    基金账户余额:50
    boy手里的钱:50
    girl钱不够取不了*/
}
import java.util.ArrayList;
import java.util.List;

// 线程不安全的集合
class UnsafeList {
    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                // 锁的是变化的量,增删改
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size()); // 10000个
    }
}
  • CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArrayList;

public class MyJUC {

    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());
    }
}

死锁

  • 四个必要条件:互斥条件、请求与保持条件、不可剥夺条件、循环等待条件
public class DeadLock {

    public static void main(String[] args) {
        Makeup girl1 = new Makeup(0,"haha");
        Makeup girl2 = new Makeup(1,"xixi");

        girl1.start();
        girl2.start();
    }
}

class Lipstick{

}

class Mirror{

}

class Makeup extends Thread{
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;
    String girlName;

    Makeup(int choice, String girlName){
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if(choice==0){
            synchronized (lipstick){
                System.out.println(this.girlName + "获得口红的锁");
                Thread.sleep(1000);
                
                //此处和上一个synchronized并列时就能破坏死锁条件
                synchronized (mirror){
                    System.out.println(this.girlName + "获得镜子的锁");
                }
            }
        }else{
            synchronized (mirror){
                System.out.println(this.girlName + "获得镜子的锁");
                Thread.sleep(2000);

                //此处和上一个synchronized并列时就能破坏死锁条件
                synchronized (lipstick){
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
        }
    }
}

Lock锁

  • 显示定义同步锁实现同步
  • jvm花费较少的时间调度线程,性能更好
  • lock只有代码块锁,synchronized有代码块和方法锁
import java.util.concurrent.locks.ReentrantLock;

class MyLock {

    public static void main(String[] args) {
        TestLock testLock = new TestLock();
        new Thread(testLock).start();
        new Thread(testLock).start();
        new Thread(testLock).start();

    }
}

class TestLock implements Runnable{
    int ticketNum = 10;

    // 定义lock锁
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while(true){
            try{
                lock.lock(); // 加锁
                if(ticketNum>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNum--);
                }else{
                    break;
                }
            }finally {
                lock.unlock(); // 解锁
            }
        }
    }
}

源码

public ReentrantLock() {
    sync = new NonfairSync(); // 默认使用非公平锁
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

线程通信

  • wait():表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
  • wait(long timeout):指定等待的毫秒数
  • notify():唤醒一个处于等待状态的线程
  • notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度
  • 以上都是Object类的方法,都只能在同步方法或者同步代码块中使用,否则抛出异常IIIegalMonitorStateException
  • 利用缓冲区:管程法

待完善。。。

  • 利用信号灯
class MyPC {
    public static void main(String[] args) {
        // 表演一个完毕后观看这个节目
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

// 生产者演员
class Player extends Thread {
    private TV tv;

    public Player(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                this.tv.play("haha");
            } else {
                this.tv.play("xixi");
            }
        }
    }
}

// 消费者观众
class Watcher extends Thread {
    private TV tv;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

// 产品节目
class TV {
    // 演员表演,观众等待 T
    // 观众观看,演员等待 F
    private String voice;
    boolean flag = true;

    // 表演
    public synchronized void play(String voice) {
        if (!flag) {
            // 观众观看上个节目,演员等待 F
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 开始表演
        System.out.println("演员表演了" + voice);
        // 通知观众观看
        this.notifyAll();// 通知唤醒
        this.voice = voice;
        this.flag = !this.flag;
    }

    // 观看
    public synchronized void watch() {
        if (flag) {
            // 演员表演,观众等待 T
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了" + voice);
        //通知演员表演
        this.notifyAll();//通知唤醒
        this.flag = !this.flag;
    }
}

线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyPool {

    public static void main(String[] args) {
        // 创建线程池 大小为10
        ExecutorService service = Executors.newFixedThreadPool(10);

        // 执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        // 关闭连接
        service.shutdown();
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
/*
    pool-1-thread-1
    pool-1-thread-3
    pool-1-thread-2
    pool-1-thread-4
*/
posted @ 2021-03-14 19:17  n1ce2cv  阅读(34)  评论(0编辑  收藏  举报