java.Thread——多线程

多线程

多线程简介

多线程Java.Thread类

进程:在操作系统中运行的程序就是进程,比如你的QQ,播放器,游戏,IDE等等。。。

  • 说起进程,就不得不说下程序,程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
  • 而进程则是执行程序的一次执行过程,它是一个动态的概念。 是系统资源分配的单位
  • 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一 个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。

本章核心概念

  • 线程就是独立的执行路径;
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程; .
  • main()称之为主线程,为系统的入口,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

多线程的创建

创建线程方式一:继承Thread类

1.继承Thread类,重写run()方法,调用start开启线程。

//总结:注意,线程开启不一定执行,由CPU调度执行。
public class ThreadTest extends Thread{
    public void run(){
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("run方法线程执行次数:"+i+"次。");
        }
    }
    public static void main(String[] args) {        //main线程,主线程
        ThreadTest threadTest = new ThreadTest();     //创建一个线程对象
        threadTest.run();       //调用run()方法,直接调用线程的run方法,就相当于调用普通方法一样。
        threadTest.start();     //调用start()方法开启线程,执行run方法的线程体,和主线程。
        for (int i = 0;i<100;i++){
            System.out.println("main主线程体执行次数:"+i+"次=======");
        }
    }
}

创建线程方式二:实现runnable接口

2.实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类对象,调用start方法。

public class RunnableTest implements Runnable{
    @Override
    public void run(){
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("run方法线程执行次数:"+i+"次。");
        }
    }
    public static void main(String[] args) {        //main线程,主线程
        RunnableTest runnableTest = new RunnableTest();//创建Runnable接口的实现类对象
//   Thread thread = new Thread(runnableTest);//创建线程对象,通过线程对象来开启我们的线程,代理
//   thread.start();//调用start方法,开启线程。
        new Thread(runnableTest).start();//上面步骤缩写成一行。
        for (int i = 0;i<1000;i++){
            System.out.println("main主线程体执行次数:"+i+"次=======");
        }
    }
}

创建线程方式三:实现Callable接口

3.实现Callable接口,重写call方法,创建执行服务,提交执行,获取结果,关闭服务

public class CallableTest implements Callable<Boolean> {
    @Override
    public Boolean call() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("线程体执行"+i+"次。");
        }
        return true;
    }
    public static void main(String[] args) {
        CallableTest callableTest1 = new CallableTest();
        CallableTest callableTest2 = new CallableTest();
        CallableTest callableTest3 = new CallableTest();
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);//创建一个池子,放3个线程。
        //ExecutorService和Executors类后面讲。
        //提交执行
        Future<Boolean> r1 = ser.submit(callableTest1); 
        Future<Boolean> r2 = ser.submit(callableTest2);
        Future<Boolean> r3 = ser.submit(callableTest3);
        //获取结果
        try {
            boolean rs1 = r1.get();
            boolean rs2 = r2.get();
            boolean rs3 = r3.get();
            System.out.println(rs1+"--"+rs2+"--"+rs3);
        } catch (Exception e) {		//抛出两个异常,直接写了Exception。
            e.printStackTrace();
        }
        //关闭服务
        ser.shutdown();
    }
}

多线程创建小结:

继承Thread类:

  • 不建议使用,避免OOP但据称局限性。

实现Runnable接口:

  • 推荐使用,避免单继承局限性,灵活多变,方便同一个的对象被多个线程使用。

实现Callable接口:

  • 了解即可。

静态代理模式

//婚礼
public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}
interface Marry{
    void HappyMarry();
}
//真实角色
class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("结婚开始");
    }
}
//代理角色
class WeddingCompany implements Marry{
    private Marry target;
    public WeddingCompany(Marry target) {
        this.target=target;
    }
    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }
    private void after() {
        System.out.println("结婚之后收尾款。");
    }
    private void before() {
        System.out.println("结婚之前,布置现场。");
    }
}

初识并发问题

public class RunnableTest2 implements Runnable{
    private int ticketNums = 10;//票数
    @Override
    public void run() {
        while(true){
            if (ticketNums<=0){
                break;
            }
            try {
                Thread.sleep(200);  //模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"张票。");
        }
    }
    public static void main(String[] args) {
        RunnableTest2 ticket = new RunnableTest2();
        new Thread(ticket,"小明").start();
        new Thread(ticket,"小强").start();
        new Thread(ticket,"黄牛党").start();
    }
}

线程暂停的方法

  1. 建议线程正常停止--->利用次数,不建议死循环。
    2. 建议使用标志位------>设置一个标志位。
    3. 不要使用stop或者destroy等过时或者JDK不建议使用的方法。

测试stop

public class TestStop implements Runnable{
//1.设置一个标识位
    private boolean flag = true;
    @Override
    public void run() {
        int a = 0;
        while(flag){
            System.out.println("run...Runnable"+a++);
        }
        System.out.println("--------------线程体结束。");
    }
//2.设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag=false;
    }
    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000;i++){
            System.out.println("main线程"+i);
            if (i==900){
                //调用stop方法切换标志位,让线程停止。
                testStop.stop();
                System.out.println("线程停止了");
            }
        }
    }
}

线程休眠:sleep()

  • sleep (时间)指定当前线程阻塞的毫秒数;

  • sleep时间达到后线程进入就绪状态;

  • sleep存在异常InterruptedException;

  • 每一个对象都有一个锁, sleep不会释放锁;

  • sleep可以模拟网络延时,倒计时等。

( 当一个方法后面声明可能会抛出InterruptedException 异常时,说明该方法是可能会花一点时间,但是可以取消的方法。 )

测试线程休眠(倒数10秒,模拟倒计时):

public class TestSleep {
    public void tenDown() throws InterruptedException{
        int num = 10;
        while(true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num<=0){
                System.out.println("计时完毕。");
                break;
            }
        }
    }
    public static void main(String[] args) {
        try {
            TestSleep s = new TestSleep();
            s.tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程礼让:Yield()

  • 礼让线程,让当前正在执行的线程暂停,但不会进入阻塞。

  • 将线程从运行状态转为就绪状态。

  • 礼让CPU重新调度,礼让不一定成功!看CPU心情。

测试线程礼让:

public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        //正常是cup想调谁都可以,礼让成功结果是a b a b a b这样,不一定成功。
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行。");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程开始执行。");
    }
}

线程强制执行:Join()

  • Join合并线程,待此线程执行完成后,再执行其他线程,其他线程为阻塞状态。

测试线程强制执行:

public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("线程vip来了。"+i);
        }
    }
    public static void main(String[] args)throws InterruptedException{
        TestJoin testJoin = new TestJoin();		//声明异常InterruptedException
        Thread thread = new Thread(testJoin);
        thread.start();
        for (int i = 0; i < 500; i++) {
            if (i==200){
                System.out.println("线程vip强制执行。===========");
                thread.join();//插队,等线程vip执行完了再执行mian线程。
            }
            System.out.println("线程main"+i);
        }
    }
}

线程的状态

public class TestState {		//观察测试线程的状态
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
        for(int i = 0; i<5; i++){		//休眠5秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("/////////");
        });
        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();//线程一旦死亡就不会能启动,线程不能启动两次
    }

线程的优先级

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
    public static void main(String[] args) {
//        打印主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        //默认状态为5。
        Thread t1 = new Thread(new MyRunnable(),"线程A");//创建线程对象并指定线程名
        Thread t2 = new Thread(new MyRunnable(),"线程B");
        Thread t3 = new Thread(new MyRunnable(),"线程C");
        //先设置优先级,在启动
        t1.setPriority(Thread.MAX_PRIORITY);//设置线程最高优先级
        t1.start();
        t2.setPriority(5);//设置线程优先级为5
        t2.start();
        t3.setPriority(Thread.MIN_PRIORITY);//设置线程最低优先级
        t3.start();
    }
}

守护(daemon)线程

线程分为用户线程和守护线程。

虚拟机必须确保用户线程执行完毕(用户线程:正常的线程都是用户线程,main线程也是。)

虚拟机不用等待守护线程执行完毕(守护线程:GC守护线程。)

GC守护线程作用:如,后台记录操作日志,监控内春,垃圾回收等待...

class Ward implements Runnable{ //ward守护线程
    @Override
    public void run() {
        while(true){
            System.out.println("ward守护线程!");
        }
    }
}
class User implements Runnable{ //user用户线程
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("user用户线程。");
        }
    }
}
public class TestDaemon {
    public static void main(String[] args) {
        Ward ward = new Ward();
        User user = new User();
        Thread thread = new Thread(ward);
        thread.setDaemon(true);//默认是用户线程,setDaemon()设置为守护线程
        System.out.println(thread.isDaemon());//判断线程是否为守护线程,是为true,否为false.
        thread.start();//守护线程就绪
        new Thread(user).start();//用户线程就绪
    }
}

线程同步

同步块方法

  • 由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是synchronized关键字,它包括两种用法:
    • synchronized方法和synchronized块.
    • 同步方法: public synchronized void method(int args) {}
  • synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法- -旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行缺陷:若将-个大的方法申明为synchronized将会影响效率

同步方法的弊端,方法里面需要修改的内容跟才需要锁,所得太多浪费资源。

同步块

同步块

  • 同步块: synchronized (Obj )

  • ◆Obj称之为同步监视器

    • Obj 可以是任何对象,但是推荐使用共享资源作为同步监视器
    • 同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this , 就是
      这个对象本身,或者是class [反射中讲解]
  • 同步监视器的执行过程

  1. 第一个线程访问,锁定同步监视器,执行其中代码.
  2. 第二个线程访问,发现同步监视器被锁定,无法访问.
  3. 第一个线程访问完毕,解锁同步监视器
  4. 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
//不安全的买票
//线程不安全,有负数
public class synTest1{
    public static void main(String[] args) {
        Syn syn = new Syn();
        new Thread(syn,"买票用户1").start();
        new Thread(syn,"买票用户2").start();
        new Thread(syn,"买票用户3").start();
    }
}
class Syn implements Runnable{
    private int ticketNums = 10 ;    //票
    boolean flag = true;	//外部停止线程方式
    @Override
    public void run() {
        while (flag){
            buy();
        }
    }
-------------------------------------------------------------------
    //使用同步锁(同步方法)解决线程不安全问题
    /*private synchronized void buy() {  		//synchronized同步方法 线程安全 锁的是this*/
--------------------------------------------------------------------
    private void buy() {
        if (ticketNums<=0){
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);	//模拟延时,放大问题
        } 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 you = new Drawing(account,50,"你");
        Drawing gilFriend = new Drawing(account,100,"gilFriend");
        you.start();
        gilFriend.start();
    }
}
class Account{		//账户
    int money;//余额
    String accountName;//卡名
    public Account(int money, String accountName) {
        this.money = money;
        this.accountName = accountName;
    }
}
class Drawing extends Thread{		//银行:模拟取款
    Account account;//账户
    int drawingMoney;//取了多少钱
    int nowMoey;//现在手机有多少钱
    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    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 = account.money - drawingMoney;    //卡内余额 = 余额 - 你取的钱
        nowMoey = nowMoey + drawingMoney;//        你手里的钱
        System.out.println(account.accountName+"余额为"+account.money);
//        Thread.currentThread().getName() = this.getName();
        System.out.println(this.getName()+"手里的钱。"+nowMoey);
-----------------------------------------------------------------------------------------------------
           //使用同步锁(同步代码块)解决线程不安全问题
        /*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 = account.money - drawingMoney;    //卡内余额 = 余额 - 你取的钱
        nowMoey = nowMoey + drawingMoney;//        你手里的钱
        System.out.println(account.accountName+"余额为"+account.money);
//        Thread.currentThread().getName() = this.getName();
        System.out.println(this.getName()+"手里的钱。"+nowMoey);
        }*/
-----------------------------------------------------------------------------------------------------
    }
}

线程不安全的集合list

//线程不安全的集合
public class list {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
-----------------------------------------------------------------------
    			//使用同步锁(同步代码块)解决线程不安全问题
                /*synchronized (list){
               		list.add(Thread.currentThread().getName());
                }*/
-----------------------------------------------------------------------
                list.add(Thread.currentThread().getName());
            }).start();
        }
        Thread.sleep(1000);
        System.out.println(list.size());//正常的是有10000,结果不对,两个线程操作了同一数据添加到了同一个位置数据覆盖掉了就会少。
    }
}

死锁

  • 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形. 某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。
//死锁:多个线程互相抱着对方需要的资源
public class DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"灰姑娘");
        Makeup g2 = new Makeup(1,"白雪公主");
        g1.start();
        g2.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;
    }
    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) {			//1秒钟后获取镜子的锁
//                    System.out.println(this.girlName+"获得镜子的锁。");
//                }
-----------------------------------------------------------------------------------------
                }
                synchronized (mirror) {		//1秒钟后获取镜子的锁
                    System.out.println(this.girlName+"获得镜子的锁。");
                }
        }else{
            synchronized (mirror){		//获取镜子的锁
                System.out.println(this.girlName+"获得镜子的锁。");
                    Thread.sleep(2000);
-----------------------------------------------------------------------------------------
//                synchronized (lipstick) {			//2秒钟后获取口红的锁
//                    System.out.println(this.girlName+"获得口红的锁。");
//				  }
-----------------------------------------------------------------------------------------
            }
                synchronized (lipstick){       //1秒钟后获取口红的锁
                    System.out.println(this.girlName+"获得口红的锁。");
                }
        }
    }
}

死锁避免方法

  • 产生死锁的四个必要条件:
  1. 互斥条件: 一个资源每次只能被一 个进程使用。
  2. 请求与保持条件: 一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件 :进程已获得的资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

Lock锁

测试

//测试Lock锁
    public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}

    class TestLock2 implements Runnable {
        int ticketNums = 10;
        //定义Lock锁
        private final ReentrantLock lock = new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();//加锁

                    if (ticketNums > 0) {
                        Thread.sleep(1000);
                        System.out.println(ticketNums--);
                    } else {
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();//解锁
                }


            }
        }
    }

生产者与消费者

线程通信:

Java提供了几个方法解决线程之间的通信问题

​ 方法名 作用

  • wait() 表示线程一-直等待,,直到其他线程通知,与sleep不同,会释放锁
  • wait(long timeout) 指定等待的毫秒数
  • notify() 唤醒一个处于等待状态的线程
  • notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

注意:均是0bject类的方法,都只能在同步方法或者同步代码块中// 使用,否则会抛出异常llegalMonitorStateException。

管程法:

//测试:生产者消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class Testscz {
    public static void main(String[] args) {
        Huanchongqu huanchongqu = new Huanchongqu();
        new Shengchan(huanchongqu).start();
        new Xiaofei(huanchongqu).start();
    }
}
//生产者
class Shengchan extends Thread{
    Huanchongqu huanchongqu;
    public Shengchan(Huanchongqu huanchongqu){
        this.huanchongqu= huanchongqu;
    }
    //生产
    @Override
    public void run(){
        for (int i = 0; i < 100; i++) {
            huanchongqu.push(new Chanpin(i));
            System.out.println("生产了"+i+"只鸡");
        }
    }
}
//消费者
class Xiaofei extends Thread{
    Huanchongqu huanchongqu;
    public Xiaofei(Huanchongqu huanchongqu){
        this.huanchongqu= huanchongqu;
    }
    //生产
    @Override
    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了--》"+huanchongqu.pop().id+"只鸡");
        }
    }
}
//产品
class Chanpin{
    int id; //产品编号
    public Chanpin(int id) {
        this.id = id;
    }
}

//缓冲区
class Huanchongqu{
    //需要一个容器大小
    Chanpin[] chanpins = new Chanpin[10];
    //容器计算器
    int count = 0;
//生产者放入产品
    public synchronized void push(Chanpin chanpin){
        //如果容器满了,就需要丢入产品
        if (count==chanpins.length){
            //通知消费者消费,生产等待
            try {
                this.wait();		//wait()表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            //如果没有满,我们就需要丢入产品
        chanpins[count]=chanpin;
        count++;
//        可以通知消费者消费了
        this.notifyAll();		//唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
    }

//消费者消费
    public synchronized Chanpin pop(){
        //判断能否消费
        if (count==0){
            //等待生产者生产,消费者等待
            try {
                this.wait();		//wait()表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
//        如果可以消费
        count--;
        Chanpin chanpin = chanpins[count];
//        吃完了,通知生产者生产
        this.notifyAll();		//唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
        return chanpin;
    }
}

信号灯法:

//测试生产者消费者问题2:信号灯法,标志位解决
public class Testsc2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Shengchan1(tv).start();
        new Xiaofei1(tv).start();
    }
}

//生产者-->演员
class Shengchan1 extends Thread {
    TV tv;
    public Shengchan1(TV tv){
        this.tv=tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("快乐大本营");
            }else {
                this.tv.play("抖音:记录美好生活。");
            }
        }
    }
}

//消费者-->观众
class Xiaofei1 extends Thread{
    TV tv;
    public Xiaofei1(TV tv){
        this.tv=tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品-->节目
class TV{
    //演员老演,观众等待  T
    //观众观看,演员等待  F
    String voice; // 表演的节目
    boolean flag = true ;
    //表演
    public synchronized void play(String voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:"+voice);
        //通知观众观看
        this.notifyAll();       ///唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
        this.voice=voice;
        this.flag = !this.flag;
    }

    //观看
    public synchronized void watch() {
        if (flag){
            try {
                this.wait() ;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了:"+voice);
//.通知演员表演
        this.notifyAll();   //唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
        this.flag = !this.flag;
    }
}

线程池

使用线程池

  • 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
  • 好处:
    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 便于线程管理(....)
    • corePoolSize:核心池的大小
    • maximumPoolSize: 最大线程数
    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

使用线程池

  • JDK 5.0起提供了线程池相关API: ExecutorService和Executors

  • ExecutorService: 真正的线程池接口。常见子类ThreadPoolExecutor

    • void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执
      行Runnable
    • Future submit(Callable task):执行任务,有返回值,一般又 来执行
      Callable
    • void shutdown() :关闭连接池
  • Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池

public class TestPool{
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);//1..创建服务,创建按线程池为10个线程
        service.execute(new MyThread());//执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.shutdown();	// 2.关闭连接
    }
}
 class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

总结:ExecutorService是线程池接口,通过Executors创建一个池子大小, execute一般用来执
行Runnable;submit,有返回值,一般又 来执行Callable。

posted @ 2020-01-12 12:06  渝思  阅读(207)  评论(0编辑  收藏  举报