• 牛啊牛啊!这篇多线程技术笔记,阿里架构师看了都说好!


    程序,进程,线程

    • 程序:为完成特定任务,使用某种语言编写的一组指令的集合,是一段静态的代码。
    • 进程:程序的一次运行过程,或者是正在运行的一个程序。进程是资源分配的基本单位。
    • 线程:线程由进程进一步细化而来,是一个程序内部的一条执行路径。线程是调度和执行的单位,每个线程拥有独立的运行栈和程序计数器,线程开销很小。

    Thread生命周期

    Thread类中的常用方法

    • start():启动当前线程;调用当前线程的run()。
    • run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中。
    • currentThread():静态方法,返回执行当前代码的线程。
    • getName():获取当前线程的名字。
    • setName():设置当前线程的名字。
    • yield():释放当前cpu的执行权。
    • join():在线程a中调用线程b的join()方法,此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
    • sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
    • isAlive():判断当前线程是否存活。
    • getPriority():获取线程的优先级。
    • setPriority(int p):设置线程的优先级。
    • 线程的优先级有以下三种,MAX_PRIORITY:10;MIN _PRIORITY:1;NORM_PRIORITY:5。

    创建多线程的方式

    继承Thread类

    步骤:

    • 创建一个继承于Thread类的子类;
    • 重写Thread类中的run()方法,将次线程执行的操作声明在run()方法中。
    • 创建Thread类的子类对象。
    • 通过此对象调用start()。
      举例:三个窗口进行卖票(存在线程安全问题)
    class MThread extends Thread{
        private static int tickets = 100;
        @Override
        public void run() {
            while(true){
                if(tickets > 0){
                    System.out.println(getName() + "卖票,票号为:" + tickets--);
                }else{
                    break;
                }
            }
        }
    }
    public class ThreadTest {
        public static void main(String[] args) {
            MThread t1 = new MThread ();
            MThread t2 = new MThread ();
            MThread t3 = new MThread ();
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    

    说明:局限于类的单继承性。

    实现Runnable接口

    步骤:

    • 创建一个实现Runnable接口的类。
    • 实现类去实现Runnable中的抽象方法:run()。
    • 创建实现类的对象。
    • 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象。
    • 通过Thread类的对象调用start()。
      举例:三个窗口进行卖票(存在线程安全问题)
    class MThread implements Runnable{
       private int tickets = 100;
       @Override
       public void run() {
           while(true){
               if(tickets > 0){
                   System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + tickets--);
               }else{
                   break;
               }
           }
       }
    }
    public class ThreadTest {
       public static void main(String[] args) {
           MThread m = new MThread();
           Thread t1 = new Thread(m);
           Thread t2 = new Thread(m);
           Thread t3 = new Thread(m);
           t1.setName("窗口1");
           t2.setName("窗口2");
           t3.setName("窗口3");
           t1.start();
           t2.start();
           t3.start();
       }
    }
    
    

    说明:不会局限于类的单继承性;适合处理多个线程共享数据的情况。

    实现Callable接口

    步骤:

    • 创建一个实现Callable的实现类。
    • 实现call方法,将此线程需要执行的操作声明在call()中。
    • 创建Callable接口实现类的对象。
    • 将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象。
    • 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()。
      举例:遍历100以内的偶数,并且计算他们的和
    class MThread implements Callable{
        @Override
        public Object call() throws Exception {
            int sum = 0;
            for (int i = 2; i < 101; i+=2) {
                    System.out.println(i);
                    sum += i;
                }
            }
            return sum;
        }
    }
    
    
    public class ThreadTest {
        public static void main(String[] args) {
            MThread thread = new MThread ();
            FutureTask futureTask = new FutureTask(thread);
            new Thread(futureTask).start();
            try { 
                //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
                Object sum = futureTask.get();
                System.out.println("总和为:" + sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    
    }
    
    
    

    说明:call()方法有返回值,并且可以抛出异常;callable支持泛型。

    线程池

    步骤:

    • 提供指定线程数量的线程池。
    • 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象。
    • 关闭线程池。
      举例:两个线程遍历100以内的奇偶数
    class NumberThread implements Runnable{
    
        @Override
        public void run() {
            for(int i = 1;i < 101; i+=2){
                 System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
    
    class NumberThread1 implements Runnable{
    
        @Override
        public void run() {
            for(int i = 2;i < 101; i+=2){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
    
    public class ThreadPool {
        public static void main(String[] args) { 
            ExecutorService service = Executors.newFixedThreadPool(10);
            ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
            service.execute(new NumberThread());//适用于Runnable
            service.execute(new NumberThread1());//适用于Runnable
    
    		//service.submit(Callable callable);//使用于Callable
            service.shutdown();
        }
    }
    
    
    

    说明:提高了响应速度;降低资源消耗;便于线程管理。

    线程同步机制

    解决线程安全问题

    同步代码块

    synchronized(对象){//需要被同步的代码}
    举例:三个窗口卖票

    class Windows extends Thread{
        private static int tickets = 100;
        @Override
        public void run() {
            while(true){
                synchronized(Windows.class){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(tickets > 0){
                        System.out.println(getName() + "卖票,票号为:" + tickets--);
                    }else{
                        break;
                    }
                }
            }
        }
    }
    public class Test {
        public static void main(String[] args) {
            Windows t1 = new Windows();
            Windows t2 = new Windows();
            Windows t3 = new Windows();
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    
    

    同步方法

    将synchronized放到方法的声明中。
    举例:三个窗口卖票。

    class Windows implements Runnable {
        private int tickets = 100;
        @Override
        public void run() {
            while(true){
                show();
            }
        }
        private synchronized void show(){
            if(tickets > 0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + tickets--);
            }
        }
    
    }
    public class Test{
        public static void main(String[] args) {
            Windows t = new Windows();
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            Thread t3 = new Thread(t);
            t1.setName("窗口1");
            t2.setName("窗口2");
            t3.setName("窗口3");
            t1.start();t2.start();t3.start();
        }
    }
    
    
    

    Lock

    class A{
    	private final ReentrantLock lock = new ReentrantLock();
    	public void m(){
    		lock.lock();
    		try{
    			//保证线程安全的代码
    		}
    		finally{
    			lock.unlock();
    		}
    	}
    }
    
    

    举例:三个窗口卖票

    class Window implements Runnable{
        private int ticket = 100;
        private ReentrantLock lock = new ReentrantLock();
        @Override
        public void run() {
            while(true){
                try{
                    lock.lock();
                    if(ticket > 0){
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket--);
                    }
                }finally {
                    lock.unlock();
                }
            }
        }
    }
    public class LockTest {
        public static void main(String[] args) {
            Window w = new Window();
            Thread t1 = new Thread(w);
            Thread t2 = new Thread(w);
            Thread t3 = new Thread(w);
            t1.setName("线程1");t2.setName("线程2");t3.setName("线程3");
            t1.start();t2.start();t3.start();
        }
    }
    
    
    

    synchronized和lock对比:

    • lock是显示锁,手动开启和关闭;synchronized是隐式锁,出了作用域自动释放。
    • lock只有代码块锁;synchronized有代码块锁和方法锁。
    • 使用lock锁,JVM将花费较少的时间来调度线程,性能更好;并且具有更好的扩展性。

    线程通信

    三个方法

    • wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
    • )notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
    • notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

    说明

    • wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
    • wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常。
    • wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。

    举例:生产者消费者问题

    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 consumeProduct() {
            if(productCount > 0){
                System.out.println(Thread.currentThread().getName() + "消费第" + productCount + "个产品");
                productCount --;
                notify();
            }else {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    class Productor extends Thread{
        private Clerk clerk;
    
        public Productor(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.consumeProduct();
            }
        }
    }
    public class ProductTest {
        public static void main(String[] args) {
            Clerk clerk = new Clerk();
            Productor p1 = new Productor(clerk);
            p1.setName("生产者1");
            Consumer c1 = new Consumer(clerk);
            c1.setName("消费者1");
            p1.start();c1.start();
        }
    }
    
    
    

    最后

    在文章的最后作者为大家整理了很多资料!包括java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书等等!

    全部免费分享给大家,欢迎关注公众号:前程有光领取!

  • 相关阅读:
    【BZOJ3993】星际战争(SDOI2015)-二分答案+最大流
    【BZOJ3996】线性代数(TJOI2015)-最小割
    【BZOJ3996】线性代数(TJOI2015)-最小割
    【APIO2010T2】巡逻-贪心+树形DP
    【APIO2010T2】巡逻-贪心+树形DP
    【NOIP2016提高组T2】天天爱跑步-倍增LCA+树上差分
    【NOIP2016提高组T2】天天爱跑步-倍增LCA+树上差分
    【POJ2411】Mondriaan's Dream-状态压缩DP(插头DP?)
    【POJ2411】Mondriaan's Dream-状态压缩DP(插头DP?)
    【POJ1679】The Unique MST-次小生成树(判断最小生成树唯一性)
  • 原文地址:https://www.cnblogs.com/lwh1019/p/14545910.html
Copyright © 2020-2023  润新知