多线程(4)

    技术2022-07-11  101

    目录

    一、线程的通信1.涉及到的三个方法:2.说明:3.线程通信的应用:4.释放锁的操作5.不释放锁的操作 二、实现Callable接口三、使用线程池


    一、线程的通信

    1.涉及到的三个方法:

    三个方法必须使用在同步代码块或同步方法中。三个方法的调用者必须是同步代码块或同步方法中的同步监视器中的,否则,会出现IlLegaLMonitorStateException异常。三个方法是定义在java.lang.object类中。

    2.说明:

    wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。notify():一旦执行此方法,就会唤醒被wait的一个线程。 如果有多个线程被wait,就唤醒优先级高的那个。notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。 /* 线程通信问题的例子:使用两个线程打印1-100.线程1,线程2交替打印 */ public class CommunicationTest { public static void main(String[] args) { Number number = new Number(); Thread t1 = new Thread(number); Thread t2 = new Thread(number); t1.setName("线程1"); t2.setName("线程2"); t1.start(); t2.start(); } } class Number implements Runnable{ private int number = 1; @Override public void run() { while (true){ synchronized (this) { notify(); if (number <= 100){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() +":" + number); number++; try { //使得调用如下wait()方法的线程进入阻塞状态 wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ break; } } } } }

    3.线程通信的应用:

    经典例题:生产者/消费者问题说明: 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。

    分析: 1.是否是多线程问题? 是,生产者线程,消费者线程 2.是否有共享数据? 是,店员(或产品) 3.如何解决线程的安全问题? 同步机制,有三种方法 4.是否涉及线程的通信? 是

    public class ProductTest { public static void main(String[] args) { Clerk clerk = new Clerk(); Producer producer = new Producer(clerk); producer.setName("生产者"); Consumer customer = new Consumer(clerk); customer.setName("消费者1"); // Consumer customer1 = new Consumer(clerk); // customer1.setName("消费者2"); producer.start(); customer.start(); // customer1.start(); } } //店员 class Clerk{ private int productCount = 0; //生产产品 public synchronized void produceProduct() { if (productCount < 20){ productCount++; System.out.println(Thread.currentThread().getName() + ":开始生产第:" + productCount +"个产品"); notify(); }else{ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } //消费产品 public synchronized void consumerProduct() { if (productCount > 0){ System.out.println(Thread.currentThread().getName() + ":开始消费第:" + productCount +"个产品"); productCount--; notify(); }else{ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer extends Thread{//生产者 private Clerk clerk; public Producer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { System.out.println(Thread.currentThread().getName()+ ":开始生产产品..."); while (true){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } clerk.produceProduct(); } } } class Consumer extends Thread{//消费者 private Clerk clerk; public Consumer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { System.out.println(Thread.currentThread().getName()+ ":开始消费产品..."); while (true){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } clerk.consumerProduct(); } } }

    4.释放锁的操作

    当前线程的同步方法、同步代码块执行结束。当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。

    5.不释放锁的操作

    线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行。线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。应尽量避免使用suspend()和resume()来控制线程

    二、实现Callable接口

    创建线程的方式三:实现Callable接口。— JDK 5.0新增

    实现Callable接口的步骤如下: .创建一个实现Callable的实现类 2.实现call方法,将此线程需要执行的操作声明在call()中 3.创建Callable接口实现里类的对象 4.将此Callable接口实现类对象作为传递到FutureTask构造器中,创建FutureTask的对象 5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法 6.获取Callable中call方法的返回值

    代码实现:

    public class ThreadNew { public static void main(String[] args) { //3.创建Callable接口实现里类的对象 NumThread numThread = new NumThread(); //4.将此Callable接口实现类对象作为传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()方法 new Thread(futureTask).start(); try { //6.获取Callable中call方法的返回值 //get()返回值即为FutureTask构造器在参数Callable实现类重写的call()的返回值。 Object num = futureTask.get(); System.out.println("总和为:"+ num); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } //1.创建一个实现Callable的实现类 class NumThread implements Callable{ //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Object call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { if (i % 2 == 0){ System.out.println(i); sum +=i; } } return sum; } }

    三、使用线程池

    创建线程的方式四:使用线程池

    好处: 1.提高响应速度(减少了创建新线程的时间) 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建) 3.便于线程管理

    corePoolsize:核心池的大小maximumPoolsize:最大线程数keepAliveTime:线程没有任务时最多保持多长时间后会终止

    代码实现:

    public class ThreadPool { public static void main(String[] args) { //1.提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //设置线程池的属性 System.out.println(service.getClass()); // service1.setMaximumPoolSize(15); // service1.setKeepAliveTime(10,TimeUnit.DAYS); //2.执行指定的线程的操作。需要提供实现Runnable接口或者Callable接口实现类的对象 service.execute(new NumberThread());//适合适用于Runnable service.execute(new NumberThread1());//适合适用于Runnable // service.submit();//适合使用于Callable //3.关闭连接池 service.shutdown(); } } class NumberThread implements Runnable{ @Override public void run() { for (int i = 0; i <= 100; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() +":"+i); } } } } class NumberThread1 implements Runnable{ @Override public void run() { for (int i = 0; i <= 100; i++) { if (i % 2 != 0){ System.out.println(Thread.currentThread().getName() +":"+i); } } } }

    多线程知识的学习

    多线程(1):https://blog.csdn.net/weixin_45606067/article/details/107068938 多线程(2):https://blog.csdn.net/weixin_45606067/article/details/107067802 多线程(3):https://blog.csdn.net/weixin_45606067/article/details/107067857


    如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。 创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客
    Processed: 0.016, SQL: 9