JAVA开发之旅(十五)###多线程###

###多线程###

 

1. 基本概念

进程:

  程序(任务)的执行过程 ---> 动态性(必须是可执行文件执行后)

  持有资源(共享内存,共享文件)和线程 --->载体

线程:

  源代码文本编辑

  源代码编译

  语法校验

  ... ...

  线程是系统中最小的执行单眼,同一个进程中有多个线程,线程共享进程的资源。

线程的交互:互斥、同步  

 

2. 线程中的常用方法

  1)Java对线程的支持

  

Thread常用方法:

  线程的创建: 

                   Thread()

                   Thread(String name)

                   Thread(Runnable target)

                   Thread(Runnable target,String name)

  线程的方法:

                 void start()    --->启动线程

                 static void sleep(long millis)    --->线程休眠

                 static void sleep(long millis , int nanos)   --->线程休眠

                 void join()   --->

                 void join(long millis)   --->                    使其他线程等待当前线程终止  

                 void join(long millis , int nanos)   --->

                 static void yield()   --->当前运行线程释放处理器资源

  获取线程的引用:

                 static Thread currentThread()   --->返回当前运行的线程引用

 

例:需求设置一个舞台剧

1. 主角设定

单线程运行:

package homework;

import com.sun.org.apache.xerces.internal.dom.PSVIAttrNSImpl;
import sun.applet.Main;

public class Actor extends Thread{
    public void run(){
        System.out.println(getName()+"我是一个演员");
        int count = 0;
        boolean keepRunning = true;
        while (keepRunning) {
            if(count == 10){
                keepRunning = false;
            }
            if(count%5 == 0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(getName() + "登台演出" + (++count));
        }
        System.out.println(getName()+"的演出结束了!");
    }
}
    public static void main(String[] args) {
        Thread actor = new Actor();
        actor.setName("Mr Thread");
        actor.start();
    }

双线程运行:

    static class Actress implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"我是一个演员");
            int count = 0;
            boolean keepRunning = true;
            while (keepRunning) {
                if(count == 10){
                    keepRunning = false;
                }
                if(count%5 == 0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "登台演出" + (++count));
            }
            System.out.println(Thread.currentThread().getName()+"的演出结束了!");
        }
    }
    public static void main(String[] args) {
        Thread actor = new Actor();
        actor.setName("Mr Thread");
        actor.start();

        new Thread(new Actress(),"Ms.Runnable").start();
    }

 

2. 群演

package homework;

//大舞台
public class Stage extends Thread {
    public void run() {
        QunYan qunYan1 = new QunYan();
        QunYan qunYan2 = new QunYan();
        //创建Runnable接口
        Thread qunY1 = new Thread(qunYan1, "路人卖糖葫芦的");
        Thread qunY2 = new Thread(qunYan2, "路人甲");
        //启动线程,让群演上场
        qunY1.start();
        qunY2.start();
        //舞台线程休眠
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        qunYan1.keepRuning = false;
        qunYan2.keepRuning = false;
        try {
            qunY1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
    public static void main(String[] args) {

        new Stage().start();
    }

 

3. 主角登场

package homework;

public class Zhujue extends Thread{
    public void run(){
        System.out.println(Thread.currentThread().getName()+"该上场了");
        for(int i =0; i<5;i++){
            System.out.println(Thread.currentThread().getName()+"开始了他的表演");
        }
        System.out.println(Thread.currentThread().getName()+"退场了");
    }
}
package homework;

//大舞台
public class Stage extends Thread {
    public void run() {
        System.out.println("欢迎观看热巴大舞台");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        QunYan qunYan1 = new QunYan();
        QunYan qunYan2 = new QunYan();
        //创建Runnable接口
        Thread qunY1 = new Thread(qunYan1, "路人卖糖葫芦的");
        Thread qunY2 = new Thread(qunYan2, "路人甲");
        //启动线程,让群演上场
        qunY1.start();
        qunY2.start();
        //舞台线程休眠
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("正当路人都没有什么表演时,主角上场了");
        Thread zj = new Zhujue();
        zj.setName("迪丽热巴");
        System.out.println("热巴上场,观众欢呼!");
        //停止线程方法
        qunYan1.keepRuning = false;
        qunYan2.keepRuning = false;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        zj.start();
        try {
            zj.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("迪丽热巴登场增加了舞台的精彩,完美了舞台剧");
    }
}
 public static void main(String[] args) {

        new Stage().start();
    }

 

2. 如何正确的停止Java中的线程

  1)stop()方法:禁用,会使线程立刻停止,无法得知线程进行到哪一步,也无法完成清理工作

  ---------------------------------------------------------错误的方法

  2)如何停止线程:使用退出的标志

       比如:我们利用boolean类型结束循环去停止线程,这样可以完整的执行完线程后停止,也可以有充足的时间去清理

 

 

3. 线程间的交互

  1)争用条件:当多个线程同时共享访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这种现场称为争用条件。  

  2)互斥与同步:

    synchronized ()

 

  同步实现:wait() / notify() / notifyAll()

 

例:

package homework;

public class ThreadTest {
    static Object obj = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (obj) {
                System.out.println("thread-0线程执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread-0其它代码....");
            }
        }).start();

        new Thread(() -> {
            synchronized (obj) {
                System.out.println("thread-1线程执行....");
                try {
                    obj.wait(); // 让线程在obj上一直等待下去
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread-1其它代码....");
            }
        }).start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("唤醒obj上其它线程");
        synchronized (obj) {
            obj.notifyAll();
        }
    }
}

 

4. 线程状态

  NEW(新建)  线程刚被创建,但是还没有调用 start方法

  RUNNABLE(可运行) 当调用了start() 方法之后

  BLOCKED(阻塞) 当线程进入了monitor监视器区,处于entrySet里准备竞争锁的时候,处于阻塞状态

  WAITING(等待) 当调用了对象的wait方法,或调用了线程对象的join方法,进入了WaitSet,处于等待状态

  TIMED_WAITING 当调用wait(long n) join(long n) 进入了WaitSet,处于有限时的等待状态
  当调用sleep(long n) 是让当前线程放弃cpu的时间片,睡眠一会

  TERMINATED (终止)当线程代码运行结束

 

5. 线程池

创建有限的线程资源为更多的任务提供服务

一个核心的ExecutorService的实现类:ThreadPoolExecutor

ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)

corePoolSize 核心线程数目 (最多保留的线程数)

maximumPoolSize

workQueue 阻塞队列 如果任务超过了核心线程数,进入队列进行排队,直到有空闲的线程

如果任务过多,阻塞队列都放不下了,还会创建新的线程来救急

corePoolSize+救急的线程 <= maximumPoolSize(最大线程数)会抛出拒绝提交任务异常 

keepAliveTime 生存时间- 针对救急线程

unit 时间单位 秒

创建固定大小的线程池:

Executors.newFixedThreadPool(2);

核心线程数=最大线程数(没有救急线程被创建)

阻塞队列  无界,可以放任意数量的任务,

适合执行数量有限,长时间运行的任务

创建缓冲线程池

Executors.newCachedThreadPool()

核心线程数是0, 最大线程数是Integer的最大值(救急线程可以无限创建)

生存时间是60s

适合任务数比较密集,但每个任务执行时间较短的情况

创建单线程线程池

Executors.newSingleThreadExecutor()

使用场景:希望多个任务排队执行

区别:

Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改

Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改

 

经典例题:生产者消费者

package homework;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class Product{

    public Product(int i) {
        
    }
}
public class ThreadTest2 {
    private static BlockingQueue<Product> queue = new ArrayBlockingQueue<>(5);
    public static void main(String[] args) {

        // 生产者线程
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                Product p = new Product(i);
                System.out.println(Thread.currentThread().getName()+"生产了:"+p);
                try {
                    queue.put(p);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        // 消费者线程
        for (int j = 0; j < 5; j++) {
            new Thread(()->{
                for (int i = 0; i < 2; i++) {
                    try {
                        Product p = queue.take();
                        System.out.println(Thread.currentThread().getName()+"消费了:"+p);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

 

 

 

 

####END####
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值