多线程基础(线程创建、线程状态、线程同步、通信、线程池)

多线程基础(线程创建、线程状态、线程同步、通信、线程池)

  • 线程的创建与实现
    • 创建线程的三种方式
      • 1. 继承Thread类
      • 2. 实现Runnable接口
      • 3. 实现Callable接口
  • 线程状态
    • 线程的六大状态
    • 线程停止
    • 线程休眠_sleep
    • 线程礼让_yield
    • 线程强制执行_join
    • 线程优先级
  • 线程同步的方式(队列加锁)
    • 1.使用synchronized
    • 2.使用Lock
    • synchonized与lock的区别
  • 死锁
  • 线程通信问题
    • 生产者消费者问题
    • 信号灯法
  • 线程池

此篇文章均为线程基础,并未深入剖析

线程的创建与实现

创建线程的三种方式

1. 继承Thread类

实现:继承Thread类,重写run()方法,调用start开启线程

public class ThreadStudy extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("run方法正在执行" + i);
        }
    }
    //测试
    public static void main(String[] args) {

        ThreadStudy threadStudy = new ThreadStudy();
        //如果调用run方法,那么会按顺序执行,先输出100遍run方法正在执行,再输出main方法正在执行
        //threadStudy.run();
        //如果调用start方法,那么就会穿插执行,取决于cpu
        threadStudy.start();

        for (int i = 0; i < 1000; i++) {
            System.out.println("main方法正在执行" + i);
        }
    }
}

2. 实现Runnable接口

实现:实现Runnable接口,采用Thread静态代理,调用start方法

public class Threadtest3 implements Runnable{

    private int ticketNum = 10;
    @Override
    public void run() {
        while(true) {
            if (ticketNum <= 0) break;

            try {
                Thread.sleep(200l);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +"--拿到了第" + ticketNum-- + "张票");
        }
    }
    //测试
    public static void main(String[] args) {
        Threadtest3 threadtest3 = new Threadtest3();
        //Thread静态代理
        new Thread(threadtest3, "小明").start();
        new Thread(threadtest3, "小杨").start();
        new Thread(threadtest3, "小张").start();
    }
}

3. 实现Callable接口

1.实现Callable接口,需要返回值类型,返回值即创建按执行服务ExecutorService中submit的返回值
2.重写call()方法
3.创建目标对象
4.创建执行服务ExecutorService es = Executors.newFixedThreadPool(num);
5.提交执行Future result = es.submit(target);
6.获取结果Boolean flag = result.get();
7.关闭服务es.shutdownNow();

public class TestCallable implements Callable {

    @Override
    public Object call() throws Exception {
        System.out.println("多线程之实现Callable创建线程");
        return true;//这个的return值,就是下边ExecutorService的submit的方法的返回值
    }
    //测试
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建目标对象
        TestCallable target = new TestCallable();
        //创建执行任务
        ExecutorService es = Executors.newFixedThreadPool(3);
        //提交执行,泛型的类型根据call方法的返回值决定
        Future<Boolean> result = es.submit(target);
        //获取执行结果
        Boolean flag = result.get();
        System.out.println(flag);
        //关闭服务
        es.shutdownNow();
    }
}

线程状态

线程的六大状态

线程六个状态是Thread类中枚举创建的public enum State {…}

  • 1.创建状态 NEW
  • 2.就绪状态 RUNNABLE
  • 3.阻塞状态 BLOCKED
  • 4.等待状态 WAITING
  • 5.定时等待 TIMED_WAITING
  • 6.终止状态 TERMINATED

线程停止

线程停止不建议使用stop(),destory()方法,建议使用线程标识停止

public class ThreadStop implements Runnable{

    //1.线程体中定义线程体使用的标识
    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        //2.线程体使用该表示
        while (flag) {
            System.out.println("run..Thread正在运行中" + i++);
        }
    }
    //3.对外提供方法改变标识
    public void stop () {
        this.flag = false;
    }


    public static void main(String[] args) {
        ThreadStop threadStop = new ThreadStop();
        new Thread(threadStop).start();
        for (int i = 0; i < 10000; i++) {
            System.out.println("main主线程目前跑到" + i);
            if (i == 9000) {
                threadStop.stop();
            }
        }
    }
}

线程休眠_sleep

new Thread().sleep(1000);

线程礼让_yield

  • 线程礼让就是让当前线程停止,但不阻塞
  • 让cpu重新调度,礼让了也不一定成功,只是会提高其他线程被cpu执行的概率
Thread.yield();

线程强制执行_join

  • Join合并线程,待此线程执行完毕后,再执行其他线程,其他线程阻塞
//这个插队就会先打出
new Thread(()-> System.out.println("插队")).join();

线程优先级

  • new Thread().getPriority();new Thread().setPriority(int priority);
  • java中定义了线程优先级最小为1,最大为10,超出会抛IllegalArgumentException()异常
  • 同样定义了优先级,并不是说优先级高的就一定会先执行,只是增加权重
  • 数值越大,权重越大
//Thread类中
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

线程同步的方式(队列加锁)

1.使用synchronized

  • 一个线程持有锁会导致其他所有需要此锁的线程挂起
  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
  • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
  • synohronized的作用域可以是方法,也可以是块
  1. synchronized加在方法上(在测试的buy方法上增加synohronized)
public class TestSynchronized{

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

        new Thread(person,"小明").start();
        new Thread(person,"小张").start();
        new Thread(person,"小李").start();
    }
}
class BuyTicket implements Runnable {
    //票
    private int ticketNum = 10;
    //线程外部停止标志
    boolean flag = true;
    @Override
    public void run() {
        //买票
        while(flag) {
            buy();
        }
    }
    //增加同步
    private synchronized void buy() {
        
        if (ticketNum <= 0) {
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.println(Thread.currentThread().getName() + "拿到第" + ticketNum-- + "张票");
    }
}
  1. synchronized加在代码块上 代码块上是锁对象,不能锁基本类型
public class TestSynchronized{

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

        new Thread(person,"小明").start();
        new Thread(person,"小张").start();
        new Thread(person,"小李").start();
    }
}
class BuyTicket implements Runnable {
    //票
    TicketNum ticketNum = new TicketNum(10);
    //线程外部停止标志
    boolean flag = true;
    @Override
    public void run() {
        //买票
        while(flag) {
            buy();
        }
    }
    //增加同步
    private void buy() {
        synchronized (ticketNum) {
            if (ticketNum.ticketNum <= 0) {
                return;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            System.out.println(Thread.currentThread().getName() + "拿到第" + ticketNum.ticketNum-- + "张票");
        }
    }
}

class TicketNum {
    int ticketNum;
    public TicketNum (int ticketNum) {
        this.ticketNum = ticketNum;
    }
}

2.使用Lock

  • 从JDK5.0开始,同步机制增加Lock对象,Lock是一个显示锁
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。所提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
  • ReentrantLock(可重复锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁
  • //定义锁 可重入锁 ReentrantLock reentrantLock = new ReentrantLock();
  • //加锁 reentrantLock.lock();
  • //解锁 reentrantLock.unlock();
public class TestLock {
    public static void main(String[] args) {
        TestLock2 lock = new TestLock2();

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

}

class TestLock2 implements Runnable {

    int ticketNum = 10;

    //定义锁 可重入锁
    ReentrantLock reentrantLock = new ReentrantLock();

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

synchonized与lock的区别

  1. Lock是显示锁(需要手动开启和关闭),synchronized是隐式锁,出了作用域,自动释放锁
  2. Lock只有代码锁,synchronized有方法锁和代码锁
  3. 优先使用Lock锁,jvm将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供了更多的子类)

死锁

  • 产生死锁的四个必要条件
  1. 互斥条件: 一个资源每次只能被一个进程使用
  2. 请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件: 进程以获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待条件: 若干线程之间形成一种头尾相接的循环等待资源关系(我手里拿着口红,我要镜子,你手里拿着镜子,你想要口红 我想要你的 之后还谁都不松手)
    例子:两个人,都要化妆,一个手里有镜子的锁,另一个手里有口红的锁,有镜子的想要口红,还不放开镜子,有口红的想要镜子,也不放开口红,就一直僵持,就造成了死锁代码中经常为一个synchronized代码块中还有一个synchronized
    代码块,一个对象手持两个或两个以上的锁
public class DeadLock {

    public static void main(String[] args) {
        MakeUp girl1 = new MakeUp(0, "女孩1");
        MakeUp girl2 = new MakeUp(2, "女孩2");

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

}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

//化妆
class MakeUp extends Thread{

    //需要的资源只有一份,用static来保证
    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 (mirror) {
//                    System.out.println(this.girlName + "获得了镜子的锁");
//                }
            }
            //出了第一个锁的代码块就不会造成死锁
            synchronized (mirror) {
                System.out.println(this.girlName + "获得了镜子的锁");
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "获得镜子的锁");
                Thread.sleep(1000);
                //代码在这个位置就会造成死锁
//                synchronized (lipstick) {
//                    System.out.println(this.girlName + "获得了口红的锁");
//                }
            }
            //出了第一个锁的代码块就不会造成死锁
            synchronized (lipstick) {
                System.out.println(this.girlName + "获得了口红的锁");
            }
        }
    }
}

线程通信问题

生产者消费者问题

生产者与消费者同时使用一个缓冲区,理想状态为,当缓冲区满时,生产者不可生产商品;当缓冲区为空时,消费者不可消费商品
生产者、消费者、缓冲区
方法

  • wait()与notify()方法
public class TestPc {

    //需要一个容器
    LinkedList<Integer> containerList = new LinkedList<Integer>();
    //容器最大容量
    private static final Integer MAX_CONTAINER = 2;

    //生产者
    class Producer implements Runnable {
        //生产者放入产品  (容器中小于2时放入,放到2时线程等待,等待消费者消费)
        @Override
        public void run() {
            synchronized (containerList) {
                //如果容器满了,就需要等待消费者消费
                if (containerList.size() == MAX_CONTAINER) {
                    System.out.println("容器已满,生产者" + Thread.currentThread().getName() + "已停止生产");
                    //生产线程等待
                    try {
                        containerList.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    //如果没满,那么生产
                    containerList.add(1);
                    System.out.println("生产者" + Thread.currentThread().getName() + "正在生产" + "容器大小为" + containerList.size());
                    containerList.notify();
                }
            }
        }
    }

    //消费者
    class Consumer implements Runnable {
        //消费者,当容器中数量不为0时,则消费,否则消费线程停止
        @Override
        public void run() {
            synchronized (containerList) {
                //如果容器为0,停止消费
                if (containerList.size() == 0) {
                    System.out.println("消费者" + Thread.currentThread().getName() + "停止消费,容器已空");
                    try {
                        containerList.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    containerList.removeFirst();
                    System.out.println("消费者" + Thread.currentThread().getName() + "开始消费,容器大小" + containerList.size());
                    containerList.notify();
                }
            }
        }
    }

    public static void main(String[] args) {
        TestPc testPc = new TestPc();
        Producer producer = testPc.new Producer();
        Consumer consumer = testPc.new Consumer();
        for (int i = 0; i < 100; i++) {
            new Thread(producer).start();
            new Thread(consumer).start();
        }
    }
}

信号灯法

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生产者
class Player extends Thread{
    TV tv;

    public Player(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (i%2==0){
                this.tv.play("节目");
            }else {
                this.tv.play("广告");
            }
        }
    }
}

//消费者
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv = tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            tv.watch();
        }
    }
}

//电视
class TV{

    boolean flag = true;  //  flag是信号灯,演员说话, 观众等待;观众观看 , 演员等待
    //节目或广告
    String voice;
    //表演
    public synchronized void play(String voice){

        //演员等待
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("表演了"+voice);
        this.voice = voice;

        //让观众观看
        this.notify();
        this.flag = !this.flag;
    }
    //观看
    public synchronized void watch(){

        //观众等待
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众看到了: "+voice);
        //通知演员表演
        this.notify();
        this.flag = !this.flag;
    }
}

线程池

  • 创建 ExecutorService es = Executors.newFixedThreadPool(nThreads);
  • 可以根据Executors工具类创建符合业务的不同线程池
  • Future submit(Runnable task);一般用来执行Callable接口的线程,因为有返回值
  • void execute(Runnable command);一般用来执行Runnable接口的线程,因为没有返回值
public class TestPool implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        ExecutorService es = Executors.newFixedThreadPool(3);
        es.execute(new TestPool());
        es.execute(new TestPool());
        es.execute(new TestPool());
        es.shutdown();
        //如果实现的是Callable接口,使用submit方法
//        es.submit(new TestPool());
//        es.submit(new TestPool());
//        es.submit(new TestPool());
//        es.shutdown();
    }
}

你可能感兴趣的:(java,多线程,java)