• 并发


    一、线程实现的方式

    1、实现Runnable接口并编写run()方法

    2、继承Thread类并覆盖run()方法

    3、前两者都不返回任何值,如果你希望任务在完成时能返回一个值,那么就需要实现Callable接口.

    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    public class ThreadTest
    {
        public static void main(String[] args)
        {
            try
            {
                Future<String> result = getDataFromRemote();
                System.out.println(result.get());
            } catch(InterruptedException | ExecutionException e)
            {
                e.printStackTrace();
            }
        }
    
        public static Future<String> getDataFromRemote()
        {
            ExecutorService service = Executors.newCachedThreadPool();
            return service.submit(new Callable<String>()
            {
                @Override
                public String call() throws Exception
                {
                    System.out.println("getDataFromRemote is ing...");
                    try
                    {
                        Thread.sleep(2000);
                    } catch(InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    return "ok";
                }
            });
        }
    }

    //output
    getDataFromRemote is ing...
    ok

    二、一些概念简介:

    Thread.yield():对线程调度器的一种建议,它在声明:“我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机”。

    Thread.sleep():休眠,不释放对CPU的占用;Object对象的wait()方法:释放CPU

    getPriority():优先级

    后台线程(daemon):也叫守护线程,是指在程序运行的时候在后台提供一些通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。当所有非后台线程结束,程序被终止,同时进程中的所有后台线程也会被杀死。典型的就是 垃圾回收。

    要将一个线程设置为后台线程,则调用setDaemon(true)即可、

    t.join():等待一段时间直到线程t结束,原线程才继续执行

    捕获异常:由于线程的本质特性,使得不能捕获从线程逃逸的异常。一旦异常逃出任务的run()方法后,就会向外传播到控制台。除非采取特殊的方式捕获这些异常。

    package threadpool;
    
    public class ExceptionThread1 implements Runnable
    {
        @Override
        public void run()
        {
            throw new RuntimeException();
        }
    
        public static void main(String[] args)
        {
            try
            {
                Thread thread = new Thread(new ExceptionThread1());
                thread.start();
            } catch(Exception e)
            {
            }
        }
    }
    
    //output
    
    Exception in thread "Thread-0" java.lang.RuntimeException
        at threadpool.ExceptionThread1.run(ExceptionThread1.java:9)
        at java.lang.Thread.run(Unknown Source)

    为了解决这个问题,java se5之后的新街口Thread.UncaughtExceptionHandler可以解决这个问题。

    package threadpool;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadFactory;
    
    public class ExceptionThread implements Runnable
    {
    
        @Override
        public void run()
        {
            Thread t = Thread.currentThread();
            System.out.println("run() by " + t);
            System.out.println("eh = " + t.getUncaughtExceptionHandler());
            throw new RuntimeException();
        }
    
        public static void main(String[] args)
        {
            try
            {
                ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
                exec.execute(new ExceptionThread());
            } catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }
    
    class HandlerThreadFactory implements ThreadFactory
    {
        @Override
        public Thread newThread(Runnable r)
        {
            System.out.println(this + " creating new Thread");
            Thread t = new Thread(r);
            System.out.println(" created " + t);
            t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
            System.out.println("eh = " + t.getUncaughtExceptionHandler());
            return t;
        }
        
    }
    
    class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler
    {
        @Override
        public void uncaughtException(Thread t, Throwable e)
        {
            System.out.println("caught " + e);
        }
    }
    //output

    threadpool.HandlerThreadFactory@16acdd1 creating new Thread
    created Thread[Thread-0,5,main]
    eh = threadpool.MyUncaughtExceptionHandler@facf0b
    run() by Thread[Thread-0,5,main]
    eh = threadpool.MyUncaughtExceptionHandler@facf0b
    threadpool.HandlerThreadFactory@16acdd1 creating new Thread
    created Thread[Thread-1,5,main]
    eh = threadpool.MyUncaughtExceptionHandler@10721b0
    caught java.lang.RuntimeException

     

    三、线程同步的方式

    所谓死锁: 是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

    死锁产生的四个条件:

    1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
    3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
    4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
    基本上所有的并发模式在解决县城冲突问题的时候,都是采用序列化访问共享的方案

    1、Synchronized

    所有对象都自动含有单一的锁(也称为监视器)。当在对象上调用其任何synchronzised方法的时候,此对象都被加锁,这时候该对象的其他synchronzised方法只有等到前一个方法调用完毕并释放锁之后才能被调用。所以,对于某个特定对象来说,其所有synchronzised方法共享同一个锁。这可以被用来防止多个任务同时访问被编码为对象内存。

    针对每个类,也有一个锁。所以synchronized static 方法可以在类的范围内防止对static数据的并发访问。

    2、使用显示的Lock对象

    Java SE5的java.util.concurrent类库还包含有定义在java.util.concurrent.locks中的显示 互斥机制。Lock对象必须被显示地创建、锁定和释放。因此,它与内建的锁形式相比,代码缺乏优雅性。但是,对于解决某些类型的问题来说,它更加灵活。

    class EvenGenerator extends IntGenerator
    {
        private int currentEvenValue = 0;
        private Lock lock = new ReentrantLock();
        @Override
        public int next()
        {
            lock.lock();
            try
            {
                ++currentEvenValue;
                Thread.yield();
                ++currentEvenValue;
                return currentEvenValue;
            }
            finally
            {
                lock.unlock();
            }
        }
    }

    当使用Lock对象时,对lock()的调用,必须放置在finally子句中带有unlock()的try-finally语句中、

    return必须在try子句中,以确保unlock()不会过早发生。

    如果使用synchronized,某些事务失败了,就会抛出一个异常。而又无法去做任何清理工作,以维护系统使其处于良好状态。

    ReentrantLock允许你尝试获取但最终未获取锁,这样如果其他人已经获取了这个锁,那你就可以决定离开去执行其他一些事情,而不是等待直至这个锁被释放。

    lock.tryLock();

    lock.tryLock(2, TimeUnit.SECONDS);

    四、线程本地存储--根除对变量的共享

    线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。

    ThreadLocal类实现,通常当作静态域存储。在创建ThreadLocal时,你只能通过get()和set()方法来访问该对象的内容。实质就是每个单独的线程都被分配了自己的存储,因为它们

    每个都需要跟踪自己的计数值,从而实现了线程间的数据隔离。

  • 相关阅读:
    【力扣】11. 盛最多水的容器
    T-SQL 学习笔记 Chapter 6 子查询、表表达式 和排名函数 (一)
    忽然发现只是虚长了年岁,莫名的伤感。
    Gridview 多重表头 (二)
    那些 Cynthia 教我的事 之 PMSec (三)
    那些 Cynthia 教我的事 之 PMSec (二)
    那些 Cynthia 教我的事 之 PMSec (一)
    Gridview 多重表头 (一)
    项目总结之SSI (一)
    项目总结之MIT (一)
  • 原文地址:https://www.cnblogs.com/ld-swust/p/6529783.html
Copyright © 2020-2023  润新知