线程学习

110 阅读3分钟

一、线程状态

在这里插入图片描述

二、线程方法

在这里插入图片描述

线程停止

package com.huiqing.kuangThread;

/**
 * 线程停止
 */
public class ThreadStop {
    public static void main(String[] args) {
        MaskClass maskClass = new MaskClass();
        Thread thread = new Thread(maskClass);//静态代理
        thread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main" + i);
            if (i == 500){
                //当 i == 500 时调用stop()方法结束线程
                maskClass.stop();
                System.out.println("线程该停止了!");
            }
        }


    }
}

/**
 * 任务类
 */
 //测试stop
 //1.建议线程正常停止---->  利用次数,不建议死循环
 //2.建议使用标志位 --> 设置一个标志位 (flag)
 //3.不要使用stop或者destroy等过时方法或者JDK不建议使用的方法
class MaskClass implements Runnable{

    //1.设置一个标识位
    private boolean flag = true;
    private static MaskClass mask = new MaskClass();
    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("线程running!" + i++);
        }
    }

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

}

运行结果: 在这里插入图片描述

线程休眠

模拟网络延时,放大问题的发生性。 模拟倒计时

在这里插入图片描述

  • 例子 一 :买票
package com.huiqing.kuangThread;

//模拟网络延时,放大问题的发生性。 ,这样能更清楚地看到这三个人在抢票时发生的问题。<----------->
public class ThreadSleep {

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

        //多个线程操作同一份资源
        new Thread(buyTickets, "小明").start();
        new Thread(buyTickets, "老师").start();
        new Thread(buyTickets, "黄牛").start();

    }
}

class buyTickets extends Thread {
    private int ticketNum = 10;

    @Override
    public void run() {

        try {
            while (true) {
                if (ticketNum <= 0) {
                    break;
                }
                Thread.sleep(100);//模拟延迟
                System.out.println(Thread.currentThread().getName() + "买到了" + ticketNum-- + "张票!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}


打印结果:     (如果不加sleep大部分情况下,就被第一个线程把资源(票)抢完了)
 
小明买到了10张票!
老师买到了8张票!
黄牛买到了9张票!
小明买到了7张票!
黄牛买到了6张票!
老师买到了6张票!
老师买到了4张票!
小明买到了4张票!
黄牛买到了5张票!
小明买到了3张票!
黄牛买到了2张票!
老师买到了3张票!
黄牛买到了1张票!
老师买到了0张票!
小明买到了-1张票!     (可以看到线程存在安全性问题,不应该出现0和-1,也不应该两个人强到同一张票,之后有解决方法)

Process finished with exit code 0

  • 例子 二:模拟倒计时
package com.huiqing.kuangThread;

import java.text.SimpleDateFormat;
import java.util.Date;

public class ThreadSleep02 {
    public static void main(String[] args) {
        //获取当前时间
        Date startTime = new Date(System.currentTimeMillis());//获取当前时间

        try {
            while (true) {
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                Thread.sleep(1000);//让主线程睡上1秒
                startTime = new Date(System.currentTimeMillis());//更新时间
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }


}




部分结果:
17:19:56
17:19:58
17:19:59
17:20:00
17:20:01
17:20:02
17:20:03
17:20:04
17:20:05

线程礼让

在这里插入图片描述

package com.huiqing.kuangThread;

public class ThreadYield {

    public static void main(String[] args) {
        new Thread( new MyYield(),"a线程").start();
        new Thread( new MyYield(),"b线程").start();
    }


}
class MyYield implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始执行");
        Thread.yield();//礼让 ,不一定会成功,就是cpu让所有线程重新在进入一次就绪状态
        System.out.println(Thread.currentThread().getName() + "执行结束");
    }
}


结果:(此结果为礼让成功,每次调用yield方法不一定会成功)
b线程开始执行
a线程开始执行
b线程执行结束
a线程执行结束

线程加入 (插队)

在这里插入图片描述

package com.huiqing.kuangThread;

//线程插队
public class ThreadJoin implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程vip" + i);
        }
    }

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

        try {
            for (int j = 0; j < 500; j++) {
                if (j == 200) {
                    threadVip.join();//当主线程中 j == 200 时 ,让这个vip线程 加队
                }
                System.out.println("主线程中 : " + j);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

部分结果:
(之前一部分时两个线程同时在走,调用完join后就是ThreadVip走完,主线程才接着走)
主线程中 : 196
主线程中 : 197
主线程中 : 198
主线程中 : 199
线程vip230
线程vip231
线程vip232
线程vip233
线程vip234
线程vip235

线程状态观测

线程的六个状态 在这里插入图片描述 在这里插入图片描述

package com.huiqing.kuangThread;

public class TestThreadState {

    public static void main(String[] args) throws InterruptedException {
        //这是一个线程
        Thread thread = new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread.State state = thread.getState();
        System.out.println("线程启动前: "+thread.getState());
        thread.start();
        System.out.println("线程启动后: "+thread.getState());
        while (state != Thread.State.TERMINATED){//这要线程不终止,就更新打印状态
            Thread.sleep(700);
            System.out.println(state + " "+Thread.currentThread().getName());
            state = thread.getState();
        }
        System.out.println(state);
    }
}

线程的优先级

在这里插入图片描述

package com.huiqing.kuangThread;


public class TestThreadPriority {
    public static void main(String[] args) {
        //这个是main主线程的
        System.out.println(Thread.currentThread().getName() + "  " + Thread.currentThread().getPriority());

        Thread t1 = new Thread(new myPriority(),"t1");
        Thread t2 = new Thread(new myPriority(),"t2");
        Thread t3 = new Thread(new myPriority(),"t3");
        Thread t4 = new Thread(new myPriority(),"t4");
        Thread t5 = new Thread(new myPriority(),"t5");

        t1.setPriority(1);
        t1.start();
        t2.setPriority(5);
        t2.start();
        t3.setPriority(Thread.MAX_PRIORITY);
        t3.start();

    }
}
class myPriority implements Runnable{

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


结果:会变化,虽然设置了优先级,但优先级高的不一定会先执行
 其实线程的优先级设置可以理解为线程抢占CPU时间片的概率,虽然概率比较大,但是它不一定就是按照优先级的顺序去抢  占CPU时间片的,具体的执行顺序还是要根据谁先抢到了CPU的时间片,谁就先来执行。

    因此千万不要把设置线程的优先顺序当做是线程实际启动的优先顺序哦!

线程守护

在这里插入图片描述

package com.huiqing.kuangThread;

//上帝作为守护线程守护你
public class TestDaemon {

    public static void main(String[] args) {

        You you = new You();
        God god = new God();
        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 = 1; i < 3560; i++) {
            System.out.println("活的第 " + i + "天。");
        }
        System.out.println("世界再见!");
    }
}


class God implements Runnable {
    @Override
    public void run() {
        while (true) {//这里条件永为真,但一样会退出,当用户线程结束后jvm不会管守护线程,自己就结束了
            System.out.println("上帝守护着你!");
        }
    }
}


三、线程同步

方式一:关键字(synchronized)

使用方式: 在这里插入图片描述 第二个红框框里是同步代码块,只需要锁一部分相互竞争的资源,不用锁整个方法,相比第一种可以提高代码的效率。使用时要注意死锁问题。

方式二:加锁

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

package com.huiqing.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AccountWithoutSync {

    private static Account account = new Account();

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        for (int i = 0; i < 100; i++) {
            executor.execute(new AddAPennyTack());//执行100个线程
        }
        executor.shutdown();//执行完100个线程关闭执行器

        //判断100个线程是否执行完毕
        while (!(executor.isTerminated())) {
        }

        System.out.println("余额为:" + account.getBalance());
    }


    private static class Account {
        private static Lock lock = new ReentrantLock();//创建一个锁
        private int balance = 0;

        public int getBalance() {
            return balance;
        }

        public void deposit(int amount) {
            lock.lock();//加锁
            try {
                int newBalance = balance + amount;
                Thread.sleep(5);
                balance = newBalance;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();//解锁
            }
        }

    }

    private static class AddAPennyTack implements Runnable {

        public void run() {
            account.deposit(1);
        }
    }


}


线程为什么不安全?

给个实例:

package com.huiqing.kuangThread;

import java.util.LinkedList;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 用链表演示线程的不安全性
 */
public class UnsafeThread {

    public static void main(String[] args) {
        LinkedList<Integer> objects = new LinkedList<>();
        for (int i = 0; i < 1000; i++) {//创建1000个线程,对objects这个对象同时做add操作
            new Thread(() -> {
                objects.add(1);
            }).start();

        }

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


}

结果:
995   (本该是1000,但是同一时间多个线程会操作同一个空间,把多个值放在了一个地方,结果小于1000

JUC list线程安全处理 在这里插入图片描述

死锁问题

在这里插入图片描述

四、线程通信

管程法

在这里插入图片描述

package com.huiqing.kuangThread;



/***
 * 测试生产者消费者模型,利用缓冲区解决:管程发
 */
//生产者,消费者,产品,缓冲区
public class TestPC01 {

    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        //生产者和消费者同时对  synContainer 这个缓冲池(资源) 做操作,所以在这个资源的 push() and pop() 方法上要加锁(或synchronized)
        new Producer(synContainer,"生产者").start();
        new Consumer(synContainer,"消费者").start();


    }
}

//生产者
//任务类,继承thread 或 runnable类
class Producer extends Thread {
    SynContainer container;

    public Producer(SynContainer container,String threadName) {
        super(threadName);
        this.container = container;

    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Chicken chicken = new Chicken(i);
            container.push(chicken);
            System.out.println(Thread.currentThread().getName() + " 生产了第 " + chicken.getId() + "只鸡");
        }
    }
}

//消费者
//任务类,继承thread 或 runnable类
class Consumer extends Thread {
    SynContainer container;
//    String threadName;

    public Consumer(SynContainer container,String threadName) {
        super(threadName);
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            Chicken chicken = container.pop();
            System.out.println(Thread.currentThread().getName() + " 消费了第 " + chicken.getId());
        }
    }
}

//产品
class Chicken {
    private int id;

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


    public int getId() {
        return id;
    }
}

//缓冲区
class SynContainer {

    //容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {
        //用while不用if
        while (count == 10){
            //容器已满,通知消费者取走,生产者进入等待状态
            try {
                this.wait();//?
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        if (chickens.length == 10) {
//            //容器已满,通知消费者取走,生产者进入等待状态
//            try {
//                this.wait();//?
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//
//        }
        //容器未满,加入产品
        chickens[count] = chicken;
        count++;
        //加入之后通知消费者取走商品
        this.notifyAll();
    }

    //消费者取走商品
    public synchronized Chicken pop() {
        //用while不用if
        while (count == 0){
            //没有商品,消费者进入等待,通知生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        if (count == 0) {
//            //没有商品,消费者进入等待,通知生产者生产
//            try {
//                this.wait();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];
        this.notifyAll();
        return chicken;
    }

}