• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

多线程小小回顾

开发技术 开发技术 4小时前 1次浏览

synchronized和locked的异同

相同:都用来处理线程安全问题

不同:synchronized机制在执行完相应的同步代码块之后,自动释放同步监视器。

Lock需要手动启动 lock();方法 同步代码执行完成之后,也需要手动释放锁:unlocked()方法。

如何解决线程安全问题?几种方式?

加锁解决。三种方式:1.synchronized同步代码块,

2.synchronized同步方法

3.lock锁 (jdk1.5之后),java.util.concurrent.locks.Lock接口创建他的实现类ReentrantLock类,调用lock()、unlock();方法 加锁/释放锁。

线程通信问题

wait(); 一旦执行此方法,当前线程就会释放同步监视器,进入阻塞状态。

notify(); 一旦执行此方法,就会唤醒当前处于wait状态的线程,如果有多个线程处于wait阻塞状态,会唤醒优先级高的。

notifyAll(); 唤醒所有处于wait阻塞状态的线程。

注意:

wait,notify,notifyALL这三个方法是定义在Java.lang.Object类中的,而不是Thread类的方法。

  • 这三个方法必须使用在同步代码块中,lock都不行。
  • 这三个方法的调用者必须是同步监视器,也就是同步代码块中的synchronized(同步监视器) 或 同步方法的同步监视器,否则会出现IllegalMonitorStateException异常。(非法监视器状态异常。

Sleep和wait方法的异同。

相同:

  • 都可以让当前线程陷入阻塞状态。

不同:

  • 1.sleep方法不会释放锁,若是指定睡眠时间,在睡眠时间过后,当前线程会继续执行,wait方法会释放锁,知道notify方法将她唤醒才会继续执行。
  • 2.两个方法声明的位置不同,sleep是Thread类中的方法,而wait是Object中的方法。
  • 3.sleep方法可以在任何需要的场景下调用。但是wait方法必须是同步监视器调用的,因此必须在同步代码块或者同步方法中调用。

生产者消费者问题

package com.ene.controller;

//生产者消费者问题
public class SXQuestion {


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

        Producer producer = new Producer(clerk);
        producer.setName("生产者");

        Producer producer2 = new Producer(clerk);
        producer2.setName("生产者2");


        Custom custom = new Custom(clerk);
        custom.setName("消费者");


        producer.start();
        producer2.start();
        custom.start();
    }


}

//仓库管理员
class Clerk {
    //商品数量
    private int productNum = 0;
//    //货架容量
//    private static final int capacity = 20;

    //上架货品
    public void produceProduct() {
        synchronized (this) {
            if (productNum < 20) {
                productNum++;
                System.out.println(Thread.currentThread().getName() + ":生产第" + productNum + "件商品");

                notify();
            } else {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    //卖出货品
    public void sellProduct() {
        synchronized (this) {
            if (productNum > 0) {

                System.out.println(Thread.currentThread().getName() + ":消费第" + productNum + "件商品");
                productNum--;

                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(getName()+"开始生产产品...");

        while (true) {
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            clerk.produceProduct();
        }
    }
}


//消费者
class Custom extends Thread {
    private Clerk clerk;

    public Custom(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {

        while (true) {

            try {
                sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            clerk.sellProduct();
        }
    }
}

创建线程的四种方式:

  • 1.继承Thread类,重写run方法,调用start方法;

  • 2.实现Runnable接口,重写run方法,创建Thread类对象,实现Runnable接口的实现类作为参数传进去,例如:

    class Test1 immplements Runnable{
    	@Override
    	public void run(){
    	
    	}
    }
    
    
    public class Test{
    	   public static void main(String[] args) {
    	   Test1 test1 = new Test1();
    	   
    	   Thread myThread = new Thread(test1);
    	   
    	   
    	   }
    
    
    }
    
    
  • 3.实现Callable接口(JDK5之后)

    创建线程方式三:实现Callable接口
    
    1.创建一个实现Callable的实现类
    2.实现call方法,将此线程需要执行的操作声明在次方法中
    3.创建Callable接口实现类的对象
    4.将此对象作为参数丢到FutureTask构造器中,创建FutureTask对象
    5.将FutureTask对象作为对象传递到Thread构造器中,创建Thread对象,start()
    6.如果需要方法的返回值,则用futureTask.get()方法去获取
    
    package com.ene.dao;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    class NumThread implements Callable {
    
    
        @Override
        public Object call() throws Exception {
            List<Integer> numList = new ArrayList<>();
    
            for(int a = 1;a<10;a++){
                System.out.println(a);
                numList.add(a);
            }
            return numList;
        }
    }
    
    
    
    
    public class CallableTest{
    
    
        public static void main(String[] args) {
            //1.创建callable实现类的对象
            NumThread numThread = new NumThread();
            //2.创建FutureTask对象,把Callable的实现类作为参数丢进去
            FutureTask futureTask = new FutureTask(numThread);
            //3.创建Thread类,把futuretask创建的对象,作为参数丢进去。
            //FutureTask实现了RunnableFuture接口,然后RunnableFuture接口又继承了Runnable接口,所以能作为参数传入
            //Thread构造方法public Thread(Runnable target) 中去。
            Thread thread = new Thread(futureTask);
    
            thread.start();
        }
    
    
    }
    
  • 4.线程池创建线程(JDK5之后)

多线程小小回顾

使用Executors工具类,线程池的工厂类,创建并返回不同类型的线程池。

  • Executors.newCachedThreadPool();创建一个可根据需要创建新线程的线程池

  • Executors.newFixedThreadPool(n);创建一个可重用固定线程数的线程池

  • Executors.newSingleThreadExcutor;创建一个只有一个线程的线程池

  • Executors.newScheduledThreadPool(n);创建一个线程池,他可以安排在给定延迟后运行命令或者定期的执行。

    Cached(可根据需要创建)、Fixed(固定的)、Single(单独的)、Scheduled(有安排的)

    • 使用步骤:

      1.提供指定线程数量的线程池
      2.执行指定的线程的操作,需要提供实现Runnable接口的实现类或Callable接口的实现类的对象作为参数。
      3.关闭线程池。
      
      

    多线程小小回顾

设置线程池属性的方法。使用

ThreadPoolExecutor service = (ThreadPoolExeutor )Executors.newFixedThreadPool(10);

只有ThreadPoolExecutor里面定义了需要设置的连接池属性。ExecutorService是接口。上面图里有。


程序员灯塔
转载请注明原文链接:多线程小小回顾
喜欢 (0)