zoukankan      html  css  js  c++  java
  • 多线程

    java.exe至少有三个线程:一个main()主线程,一个GC线程(守护线程),一个异常处理线程

    一、创建多线程的三种方式

    1、通过继承Thread类(Thread实现了Runnable接口),重写run()方法;

    2、通过实现Runnable接口的run()方法;

    3、通过实现Callable接口的call()方法,支持泛型的返回值和抛出异常,要借助FutureTask来封装,既可以封装Callable,也可以封装Runnable;

    FutureTask实现了Runnable接口,支持异步获取线程执行结果,取消执行任务,异步获取执行结果会阻塞主线程;

    可以将FutureTask放入线程池中

    线程池:通过线程池来创建的优点:先创建好线程再放入线程池,可以避免频繁的创建、销毁线程,提高响应速度,减小资源消耗,便于线程管理;

    主要分为任务部分和线程部分,通过一个生产者消费者模型来将二者解耦

    任务调度过程分三步:第一步查看是否还有空闲的核心线程;第二步查看缓存队列是否满了;第三步查看最大线程的数量

    线程调度过程:抢占式调度(由系统来按优先级来决定下一个线程执行) 和 协同式调度(由线程来通知下一个线程执行)

    execute():没有返回结果,只能接收Runnable类型的任务

    submit():返回一个Future类型的对象,用来异步获取Callable任务的返回结果,可以接收Runnable和Callable任务

    shutdown()和shutdownNow()的区别

    前面三个都是通过Thread的start()方法来运行:start()启动线程,调用run()方法

    继承Thread类创建多个线程要用多个对象,一个对象不能多次调用start()

    public class MyTreadTest {
        public static void main(String[] args){
            MyThread mt = new MyThread("新线程1--");
            //mt.run();
            mt.start();  //开启多线程,开辟一个新的栈执行run()
    
            // MyThread2 mt2 = new MyThread2();
            new Thread(new MyThread2(), "新线程2--").start();;
    
            for(int i=0; i<3; i++)
                System.out.println(Thread.currentThread().getName() +" "+ i);
    
            //匿名内部类实现多线程
            new Thread(){
                @Override
                public void run(){
                    System.out.println("匿名内部类1");
                }
            }.start();
    
            new Thread(new Runnable(){
                @Override
                public void run(){
                    System.out.println("匿名内部类2");
                }
            }).start();;
        }
    }
    
    // 创建线程的第一种方式,继承Thread类
    class MyThread extends Thread{
        public MyThread(){}
        public MyThread(String name){
            super(name);
        }
    
        @Override
        public void run(){
            for(int i=0; i<3; i++)
                System.out.println(getName() + i);
        }
    }
    // 创建线程的第二种方式,实现Runable接口
    // 避免了单继承的局限性;增强了程序的扩展性
    class MyThread2 implements Runnable{
        @Override
        public void run(){
            for(int i=0; i<3; i++)
                System.out.println(Thread.currentThread().getName() +" "+ i);
        }
    }
    Thread Runnable
    /**
     * 1、Callable支持泛型
     * 2、call()可以抛出异常,后面捕获
     * 3、call()有返回值
     * */
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    class CallableThread implements Callable<Integer>{  // 支持泛型
        @Override
        public Integer call() throws Exception{
            int s = 0;
            for(int i=0; i<=100; i++){
                Thread.sleep(10);
                s += i;
            }
            return s;
        }
    }
    
    public class CallableTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CallableThread ct = new CallableThread();
            FutureTask<Integer> ft = new FutureTask<Integer>(ct);
            new Thread(ft).start();
            new Thread(ft).start();        // FutureTask只会执行一次call()
            if(!ft.isDone()){
                System.out.println("主线程执行其他任务");
            }
            System.out.println(ft.get());  // 获得call()的返回值,会阻塞主线程
            System.out.println("12345");
        }
    }
    Callable
    import java.util.concurrent.*;
    
    class NumberRun implements Runnable{
        private int s = 0;
    
        @Override  // 不能抛出异常,没有返回结果
        public void run() {
            for(int i=0; i<100; i++) {
                s += i;
            }
            System.out.println(Thread.currentThread().getName()+": "+s);
        }
    }
    
    class NumberCall implements Callable<Integer>{
        private int s = 0;
    
        @Override  // 可以抛出异常,有返回结果
        public Integer call() throws Exception {
            for(int i=0; i<100; i++){
                s += i;
            }
            System.out.println(Thread.currentThread().getName()+": "+s);
            return s;
        }
    }
    
    public class ThreadPool {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            // 线程池一:Executors.newCachedThreadPool();
            // 核心线程数为0,最大线程数为Integer.MAX_VALUE,空闲线程60s后被回收,允许创建大量线程,不添加任务
            //  手动设置线程池参数
            new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L,
                    TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    
            // 线程池二:Executors.newFixedThreadPool(5);
            // 核心线程数和最大线程数一致,允许添加大量任务
            new ThreadPoolExecutor(5, 5, 0L,
                    TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    
            // 线程池三:Executors.newSingleThreadExecutor();
            // 核心线程数和最大线程数都为1,将等待的任务放入队列,队列的最大长度为Integer.MAX_VALUE,允许添加大量任务
            new ThreadPoolExecutor(1, 1, 0L,
                    TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    
            // 线程池四:Executors.newScheduledThreadPool();
            // 也是通过ThreadPoolExecutor()实现的,可以传入一个ThreadFactory对象
    
    
            // 创建有10个线程的线程池
            ExecutorService serviceInterface = Executors.newFixedThreadPool(10);
            // ThreadPoolExecutor可以设置线程池属性
            ThreadPoolExecutor service = (ThreadPoolExecutor)serviceInterface;
    
            // 使用Runnable提交任务
            service.execute(new NumberRun());
            System.out.println("(Runnable无返回值): " + service.submit(new NumberRun()).get());
            // service.execute(new NumberCall());  // 不能提交Callable任务
            service.execute(new FutureTask<>(new NumberRun(), null));
    
            // 使用Callable提交任务
            service.submit(new NumberCall());
            // FutureTask即可传入Callable,也可传入Runnable
            service.submit(new FutureTask<Integer>(new NumberCall()));
            // execute不能提交Callable任务,但可执行经过FutureTask封装的Callable任务,因为FutureTask实现了Runnable接口
            service.execute(new FutureTask<Integer>(new NumberCall()));
            // 使用Callable提交任务,并获取返回值
            System.out.println("获取返回值: " + service.submit(new NumberCall()).get());
    
            // 不再接收新任务,等线程池中的任务执行完之后关闭线程池,
            service.shutdown();
            // 不再接收新任务,不等待,直接尝试关闭线程池
            // service.shutdownNow();
            
        }
    }
    ThreadPool

    线程的方法:

    1、Thread.currentThread():获取当前线程

    2、Thread.yield():释放当前CPU的执行权,线程进入就绪状态(下一次仍然有可能抢到执行权),不会抛出异常

    3、Thread.sleep():阻塞当前线程,线程进入阻塞状态,抛出异常

    4、join():线程a里面调用线程b的join(),则线程a进入阻塞状态,直到线程b执行完

    5、线程的优先级:getPriority()、setPriority(int p)、MIN_PRIORITY=1、MAX_PRIORITY=10、NORM_PRIORITY=5(默认)

    二、线程安全

    1、同步代码块:通过synchronized来实现,注意Thread和Runnable的同步监视器区别

    public class ThreadSecurityDemo {
        public static void main(String[] args){
            ThreadSecutiry ts = new ThreadSecutiry();
            new Thread(ts).start();
            new Thread(ts).start();
            new Thread(ts).start();
        }
    }
    
    class ThreadSecutiry implements Runnable{
        private int ticket = 100;
    
        Object obj = new Object();  // 所有对象只能拥有一把锁
    
        @Override
        public void run(){
            while(true){
                synchronized(obj){  // 传入一个锁对象,可以是任意类型,this也可以
                    if(ticket>0){
                        // try{
                        //     Thread.sleep(10);
                        // }
                        // catch(InterruptedException e){
                        //     e.printStackTrace();
                        // }
                        System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket+"张票");
                        ticket--;
                    }
                }
            }
        }
    }    
    Runnable
    class TicketThread extends Thread {
        private static int ticket = 100;
        private static Object obj = new Object();  // 保证锁唯一
    
        @Override
        public void run(){
            while(true){
                synchronized(TicketThread.class){
                    if(ticket>0) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+" 正在卖第 " +(101-ticket)+" 张票");
                        ticket--;
                    }else
                        break;
                }
            }
        }
    }
    
    public class TicketThreadTest{
        public static void main(String[] args) {
            TicketThread tt1 = new TicketThread();
            TicketThread tt2 = new TicketThread();
            tt1.start();
            tt2.start();
        }
    }
    Thread

    2、同步方法(写一个synchronized修饰的方法)

    public class ThreadSecurityDemo {
        public static void main(String[] args){
            ThreadSecutiry ts = new ThreadSecutiry();
            System.out.println(ts);
            new Thread(ts).start();
            new Thread(ts).start();
            new Thread(ts).start();
        }
    }
    
    class ThreadSecutiry implements Runnable{
        private int ticket = 100;
    
        @Override
        public void run(){
            System.out.println("this: "+this);
            while(true){
                sellTicket();
            }
        }
        // 锁对象是this
        public synchronized void sellTicket(){
            if(ticket>0){
                System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket+"张票");
                ticket--;
            }
        }
    }
    View Code
    public class ThreadSecurityDemo {
        public static void main(String[] args){
            ThreadSecutiry ts = new ThreadSecutiry();
            new Thread(ts).start();
            new Thread(ts).start();
            new Thread(ts).start();
        }
    }
    
    class ThreadSecutiry implements Runnable{
        private static int ticket = 100;
    
        @Override
        public void run(){
            while(true){
                sellTicket();
            }
        }
        // 锁对象是class文件对象
        public static synchronized void sellTicket(){
            if(ticket>0){
                System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket+"张票");
                ticket--;
            }
        }
    }
    View Code

    3、锁机制:

    java.util.cocurrrent.locks提供了Lock和ReadWriteLock接口,ReentrantLock实现了Lock接口,ReentrantReadWriteLock实现了ReadWriteLock接口,都是通过内部类Sync继承AQS实现;

    等待可中断:没有获取到锁的线程不必一直等待获取锁,可以先执行其他任务;

    可实现公平锁:线程按申请锁的顺序来获取锁,直接进入队列排队等待;非公平锁:先尝试获取锁,再进入队列排队等待,synchronized是非公平锁

    可绑定多个条件?:

    Condition condition = lock.newCondition();

    可以使用condition.await()和condition.signal()/signalAll()来阻塞或唤醒条件上线程,而不是像Object的notify()一样随机唤醒

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ThreadSecurityDemo {
        public static void main(String[] args){
            ThreadSecutiry ts = new ThreadSecutiry();
            new Thread(ts).start();
            new Thread(ts).start();
            new Thread(ts).start();
        }
    }
    
    class ThreadSecutiry implements Runnable{
        private int ticket = 100;
    
        Lock lock = new ReentrantLock();  // 所有线程只能有一把锁,如果有多个对象,这里就要设为static
    
        @Override
        public void run(){
            while(true){
                lock.lock();
                if(ticket>0){
                    try{
                        Thread.sleep(10);
                        System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket+"张票");
                        ticket--;
                    }
                    catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    finally{
                        lock.unlock();
                    }
                }
            }
        }
    }
    View Code

       4、ThreadLocal

    当多个线程使用一个共享变量时,ThreadLocal为每个使用这个变量的线程维护一个独立的变量副本,ThrealLocal依靠ThreadLocalMap来存储数据,key是ThreadLocal,value是存储的值,每个线程只有一个ThreadLocalMap

    import java.time.LocalDateTime;
    import java.util.*;
    import java.util.concurrent.*;
    
    public class ThreadLocalTest {
        private static int a = 500;
        public static void main(String[] args) {
    
            ThreadLocal threadLocal = new InheritableThreadLocal();  // 用于信息共享
            threadLocal.set("老王");
            ThreadLocal threadLocal2 = new ThreadLocal();
            threadLocal2.set("老王");
            System.out.println(threadLocal.get().equals(threadLocal2.get())); // true
            new Thread(() -> {
                System.out.println(threadLocal.get());   // "老王"
                System.out.println(threadLocal2.get());  // null
                System.out.println(threadLocal.get().equals(threadLocal2.get()));  // false
            }).start();
    
    
            new Thread(()->{
                ThreadLocal<Integer> local = new ThreadLocal<Integer>();
                while(true){
                    local.set(++a); //子线程对a的操作不会影响主线程中的a, 为什么这里是从22开始?
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("子线程:"+local.get());
                }
            }).start();
            a = 22;
            ThreadLocal<Integer> local = new ThreadLocal<Integer>();
            local.set(a);
            while(true){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("主线程:"+local.get());
            }
        }
    }
    ThreadLocal

       5、volatile关键字

    只能用来修饰多个线程共享的字段,无法修饰方法或者代码块

    volatile可以实现可见性、有序性,不能实现原子性

     

    synchronized的原理???

    ReentrantLock的原理:

    ReentrantLock位于java.util.concurrent包中,可实现公平锁和非公平锁,默认是非公平锁;

    内部有个抽象的静态内部类:Sync,Sync继承了AbstractQueuedSynchronizer(AQS)

    lock()和tryLock()的区别:lock()在获取不到锁的时候会一直等待,而tryLock()在获取不到锁的时候不会等待

    synchronized和Lock的区别?

    1、synchronized属于JVM层面,Lock属于api层面(具体的类来实现)

    2、synchronized在代码执行完之后会自动的释放锁;Lock要手动的加锁和释放锁

    3、Lock等待可中断、可实现公平锁、可绑定多个条件

    并发编程中的原子性可见性有序性:

    原子性:要么执行完,要么不执行

    可见性:一个线程修改变量之后,其他线程可立刻知道变量被修改了

    有序性:执行的顺序按照代码的先后顺序执行(会存在指令重排)

    线程的六种状态(生命周期):

    1、新建(NEW)

    2、可运行(RUNNABLE):就绪(调用start()方法,等待CPU使用权) 和 运行中(获得CPU的使用权)    yield()

    3、锁阻塞(BLOCKED):等待同步锁

    4、无限等待(WAITING):wait(),join(),LockSupport.park(),等待其他线程的唤醒

    5、计时等待(TIMED_WAITING):sleep(long time),wait(long time),join(long time)

    6、被终止(TERMINATED)

    public class WaitAndNotify {
        public static void main(String[] args){
            Object obj = new Object();
            
            // 消费者
            new Thread(){
                @Override
                public void run(){
                    synchronized(obj){
                        try{
                            System.out.println(Thread.currentThread().getName()+"进入waitting状态,释放锁对象");
                            obj.wait(); //进入无限等待状态
                        }
                        catch(InterruptedException e){
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"被唤醒");
                    }
                }
            }.start();
    
            // 生产者
            new Thread(){
                @Override
                public void run(){
                    try{
                        Thread.sleep(5000);
                    }
                    catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    synchronized(obj){
                        System.out.println(Thread.currentThread().getName()+"获取到锁对象,调用notify方法,释放锁对象");
                        obj.notify();  // 唤醒等待的线程
                    }
                }
            }.start();
        } 
    }
    View Code

    线程通性wait()/notify()

    wait():使线程进入阻塞状态,并且释放锁,sleep不会释放锁; wait()和sleep的区别? 一个来自Object和一个来自Thread

    notify():随机唤醒一个阻塞的线程,被唤醒的线程不会立即拿到锁,而是去竞争锁

    notifyAll():唤醒全部阻塞线程

    只能在同步代码块或同步方法中配套使用,并且调用者要和同步代码块或同步方法的同步监视器一致,否则导致 "IllegalMonitorStateException" 异常

    class Number implements Runnable{
        private int i = 100;
        @Override
        public void run(){
            while(true){
                synchronized (this) {   // 实现交互打印,线程通信?
    //                notify();
                    this.notifyAll();   // synchronized是this,这里也要是this,wait也要是this
                    if (i > 0) {
                        try {
                            Thread.sleep(10);  // 不会释放锁
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ": " + i--);
    
                        try {
                            this.wait();  // 会释放锁
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                    } else {
                        break;
                    }
                }
            }
        }
    }
    
    public class CommunicationTest {
        public static void main(String[] args) {
            Number number = new Number();
            new Thread(number,"n1").start();
            new Thread(number,"n2").start();
            new Thread(number,"n3").start();
        }
    }
    View Code

     

    线程中断:

    interrupt():不会中断运行中的线程(除非线程循环检测中断标志位),但是会改变中断标志位为false,有调用(sleep、wait、join等)时会抛出InterruptedException

    Thread.interrupted():判断线程是否被中断,并改变中断标志位为false,后面再调用时返回false;

    isInterrupted():只是判断线程是否已经中断;

    并不是所有的阻塞都能中断,synchronized、I/O操作等不能被中断

    try{
      while(!Thread.interrupted()){     // 非阻塞过程中断
            // do something
      }
    }catch (InterruptedException e){  // 阻塞过程中断
           
    }
    interrupt()中断
    public class InterruptTest2 {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new MyThread());
            thread.start();
            Thread.sleep(1000);
            // 注意:要循环检测中断状态才能中断线程;interrupt()只是发出中断请求,如果不检测不能退出
            thread.interrupt();
            // main线程阻塞,阻塞时中断线程会抛出异常
            thread.join(); 
            System.out.println("end");
        }
    }
    
    class MyThread implements Runnable{
        @Override
        public void run(){
            Thread hello = new Thread(new HelloThread());
            hello.start();
            try {
                hello.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("线程响应中断异常,立即中断");
            }
            // 中断hello线程,不中断仍然会运行
            hello.interrupt();
        }
    }
    
    class HelloThread implements Runnable{
        @Override
        public void run(){
            int n = 0;
            // 注意 Thread.interrupted() 和 Thread.currentThread().isInterrupted()的区别
            // Thread.interrupted() 第一次返回true并清除中断标志位,后面调用都是返回false
            // Thread.currentThread().isInterrupted()只是查询中断标志位判断是否中断
            while(!Thread.interrupted()){   
                n++;
                System.out.println("*****hello world" + n + "*****");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        }
    }
    interrupt()

    设置中断标志中断线程:

    public class InterruptTest3 {
        public static void main(String[] args) throws InterruptedException {
            HelloTest thread = new HelloTest();
            thread.start();
            Thread.sleep(1);
            thread.flag = false;
        }
    }
    
    class HelloTest extends Thread {
    
        public volatile boolean flag = true;
    
        @Override
        public void run(){
            int i = 0;
            while(flag){
                System.out.println("通过设置标志来中断当前线程" + i++);
            }
            System.out.println("end");
        }
    }
    自定义volatile类型的中断标志

    java的死锁:不同的线程分别占用对方的资源,都在等待对方释放资源

    死锁检测:

    1、jps确定java进程id

    2、jstack -pid 可以检查死锁  (此处还没成功实验)

  • 相关阅读:
    HanLP vs LTP 分词功能测试
    HanLP中文分词Lucene插件
    pyhanlp:hanlp的python接口
    Hanlp自然语言处理工具之词法分析器
    基于结构化感知机的词性标注与命名实体识别框架
    分词工具Hanlp基于感知机的中文分词框架
    Android环境下hanlp汉字转拼音功能的使用介绍
    Javascript JSON语法基础
    mui ajax方法详解
    HBuilder的webview操作
  • 原文地址:https://www.cnblogs.com/pineapple-chicken/p/14421964.html
Copyright © 2011-2022 走看看