疯狂java讲义:多线程(一)

第十六章  多线程(一)

 

    并行:在同一时刻,有多条指令在多个处理器上同时运行

    并发:同一时刻只有一个指令被处理器运行,但多个进程指令快速轮换执行,使得宏观好像多个指令在同时运行

 

    操作系统可以同时执行多个任务,每个任务就是进程;进程可以同时执行多个任务,每个任务就是线程

 

线程的创建和启动

    

    1.继承Thread类创建线程

        步骤:1.定义Thread子类,重写run(),run()即为线程执行体

                 2.创建Thread子类实例,即创建线程对象

                 3.调用线程对象的start()方法启动线程

 

        线程之间无法共享线程类的实例变量

 

    2.实现Runnable借口创建线程类

        步骤:1.定义Runnable接口,重写run()方法

                 2.创建Runnable实例,以此实例作为Thread的target来创建Thread对象,该Thread才是真正的线程对象

                可用Lambda表达式创建实例

                 3.调用线程对象的start()方法来启动线程

              eg: new Thread(RunnableThread, RunnableName).start()

 

              多线程共享实现线程类的实例变量

        

    3.使用Callable和Future创建线程

        步骤:1.创建Callable实现类,并实现call()方法

                        call()是只执行体,且有返回值

                        可使用Lambda表达式创建Callable对象

                  2.使用FutureTask类包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值

                  3.使用FutureTask对象作为Thread对象的target创建并启动新线程

                  4.使用FutureTask对象的get()方法来获得子线程执行结束后返回值

              

              eg:

                     FutureTask<Integer> task = new FutureTask<Integer>(CallableThread);

                     new Thread(task, taskName).start()

 

              call()方法可以有返回值

              call()方法可以抛出异常

 

            Future接口定义如下方法;

                boolbean cancel(boolbean mayInterrupIfRunning)    取消Future关联的callable任务

                get()    获取线程返回值,或阻塞。直到拿到返回值

                isCancelled()    如果任务正常完成前被取消,返回true

                isDone()    任务完成返回true

 

    4.创建线程的三种方式的对比

        使用Runnable、Callable接口的优缺点:

            优点:1.线程类只是实现Runnable接口和callable接口,还可以继承其他类

                      2.多个线程可以共享一个target对象,非常适合多个相同线程处理同一份资源的情况下。从而可以将CPU、代码、和数据分开。

            劣势:编程复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法

 

        使用Thread类创建线程优缺点:

            优点:编写简单,如果需要访问当前线程直接使用This

            缺点:已经继承Thread,无法再继承其他父类

 

线程的生命周期:

 

    新建new - 就绪Runnable - 运行Running - 阻塞Blocked - 死亡Dead

 

    1.new一个线程之后,是新建状态

    2.start()执行后,就绪状态

    3.如果就绪状态的线程获得CPU,开始执行run(),则是出于运行状态

    4.如下情况会进入阻塞状态:

        1.线程调用sleep()

        2.调用了阻塞式IO

        3.线程试图获得一个同步监视器,但是该同步监视器正在被其他线程占用

        4.线程在等待某个通知notify

        5.程序调用了线程的suspend()方法将该线程挂起  / 可通过resume()恢复

 

        被阻塞的线程阻塞结束后,会再次进入就绪状态,再次等待CPU,然后进入运行状态

        yield()方法可使运行状态转为就绪状态

 

    5.线程会以一下三种方式结束,结束后处于死亡状态

        1.run()、call()方法执行完毕,线程正常结束

        2.线程抛出未捕获异常和错误

        3.直接调用stop()方法结束线程

 

        可通过isAlive()方法,判断线程是否死亡

    

        Note:程序只能对新建状态的线程执行start()方法。只能执行一次。

 

控制线程:

 

    join线程:当程序调用其他线程的join()时,调用线程将会被阻塞,直到join()方法加入的join线程执行完毕,调用线程才会继续执行。

 

    后台线程:在后台运行,为其他线程提供服务。如果所有线程都死亡,后台线程会自动死亡。

          Thread对象的setDaemon(true)方法可将制定线程设置为后台线程

          isDaemon()方法用于判断线程是否为后台线程。

          前台线程创建的线程默认为前台线程,后台线程创建的线程默认为后台线程。

          要将某个线程设置为后台线程,必须在该线程启动前设置,setDaemon(true)必须在start()方法前调用

 

    线程睡眠:sleep(),让目前正在执行的线程暂停一段时间,线程进入阻塞状态,时间到了之后,进入就绪状态

    sleep()和yield()的区别:1.sleep()方法执行后,线程阻塞,然后让其他线程执行;yield()方法只会给优先级相同,或者优先级更高的线程执行机会。

                2.sleep(),让目前正在执行的线程暂停一段时间,线程进入阻塞状态,时间到了之后,进入就绪状态;yield()方法不会将线程进入阻塞状态,直接进入就绪状态。

                3.sleep()方法声明抛出InterruptedException异常,所以sleep需要捕捉该异常,或者显示声明抛出异常;而yield()没有声明抛出任何异常

                4.sleep()方法比yield()方法更具有移植性

 

    设置线程优先级:

        每个线程优先级默认与父线程优先级相同。

        main线程具有普遍优先级,main创建的子线程具有普遍优先级。

        setPriority()设置并返回线程优先级,参数整型,1~10; MAX_PRIORITY 10;MIN_PRIORITY 1; NORM_PRIORITY 5

          

线程同步:

 

    同步代码块:

        synchronized(obj)

        {

          同步代码块

        }

        程序运行行,必须先获得对同步监视器的锁定;任何时刻只有一个线程可以获得对同步监视器的锁定。

        在加锁期间其他线程无法修改该资源。

 

    同步方法:

        public/private/protect synchronized void mothodname()

        {

          方法体

        }

        synchronized不可以修饰static方法

        无需显示指定同步监视器,同步方法的同步监视器是this,也就是调用该方法的对象。

 

    synchronized可以修饰代码块和方法但是不可以修饰构造器,成员变量。

 

    同步锁:显示定义同步锁对象实现同步。

        1.定义锁对象:private final ReentrantLook lock =  new ReentrantLock();

        2.加锁:lock.lock()

        3.执行方法体

        4.释放锁:lock.unlock()

        Lock控制多个线程对共享资源进行访问。通常所提供独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源前需要先获得Lock对象。

 

ThreadLocal线程局部变量

 

    ThreadLocal:线程局部变量,为每一个使用该变量的线程都提供一个变量值的副本,使得每一个线程都可以独立的改变自己的副本,而不会与其他线程的副本产生冲突。

    T get(); 返回此局部变量在该线程中的副本值

    void remove():删除此局部变量在该线程中的值

    void set(T value):设置此局部变量在该线程中的值

 

    eg:  private ThreadLocal<String> name = new ThreadLocal();

           name.get();

           name.set("nameA");

           name.remove();

 

 

死锁

当两个线程互相等待释放同步监视器时就会发生死锁。

 

线程通信

    1.synchronized关键字

    对于使用synchronized修饰的同步方法,该类默认实例(this)是同步监视器,可以直接调用这三个方法。

    对于使用synchronized修饰的同步代码块,synchronized后的对象是同步监视器,该对象调用这三个方法。

    wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()或者notifyAll()方法唤醒该线程

    notify():唤醒此同步监视器上等待的单个线程

    notifyAll():唤醒此同步监视器上等待的所有线程

 

    2.lock()

    await():类似wait()。导致当前线程等待,直到其他线程调用该Condition的signal()或者signalAll()方法唤醒该线程

    signal():唤醒此Lock对象上等待的单个线程

    signalAll():唤醒此Lock对象上等待的所有线程

 

使用阻塞队列(BlockingQueue)控制线程通信

    BlockingQueue继承自Queue接口

    当生产者线程试图向BlockingQueue放入元素,如果队列已经满了,则该线程被阻塞;当消费者线程试图从BlockingQueue取出元素,如果队列是空的,则该线程被阻塞。

    put(E e):尝试把E元素放入BlockingQueue,如果队列已经满了,则该线程被阻塞

    take():从BlockingQueue取出元素,如果队列是空的,则该线程被阻塞

 

    有以下五个实现类:

        ArrayBlockingQueue:基于数组实现BlockingQueue

        LinkedBlockingQueue:基于链表实现BlockingQueue

        PriorityBlockingQueue:

        SynchronousQueue:同步队列。对该队列的存取操作必须交替进行

        DelayQueue:

 

线程组

    ThreadGroup表示线程组,可以对一批线程分类处理

    用户创建的线程都属于指定线程组,如果没有显示确定是哪个线程组,则属于默认线程组。默认情况下,子线程与父线程属于同一线程组。

    一旦线程加入指定线程组,线程运行中不可以改变线程组,直到死亡

 

    显式指定线程组:

    Thread(ThreadGroup group,Runnable target)

    Thread(ThreadGroup group,Runnable target,String name)  //指定线程名字

    Thread(ThreadGroup group,String name)

    

    ThreadGroup创建实例

    ThreadGroup(String name)

    ThreadGroup(ThreadGroup parent, String name)

 

    int activeCount()  //返回此线程中活动线程的数目

    interrupt()    //中断该线程组所有线程

    isDaemon()    //判断该线程是否是后台线程组

    setDaemon(boolean daemon)    //把该线程设置为后台线程组

    setMaxPriority(int pri)        //设置线程组的最高优先级

 

线程池

    返回ExecutorService对象,该对象代表一个线程池:

    newCachedThreadPool()创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

    newFixedThreadPool(int nThreads) 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

    newSingleThreadExecutor() 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

 

    返回ScheduledExecutorService对象,该对象代表一个线程池:

    newScheduledThreadPool(int corePoolSize) 创建一个定长线程池,支持定时及周期性任务执行。

    newSingleThreadScheduledExecutor 创建只有一个线程的线程池,可以在指定延时后执行线程任务

 

    ExecutorService newWorkStealingPool(int parallelism) 创建持有足够线程的线程池来支持给定的并行级别,该方法还会使用多个队列来减少竞争

    ExecutorService newWorkStealingPool(int parallelism) 前一个简化版本,例如4个cpu,则并行级别设置为4

 

    ExecutorService代表尽快执行的线程池,只要有空闲线程就执行任务。

        Future<?> submit(Runnable task):将一个Runnable对象提交给指定线程池,在有空闲线程时执行。因为run()方法没有返回值,所有该方法返回null

        <T>Future<T> submit(Runnable task, T result):将一个Runnable对象提交给指定线程池,在有空闲线程时执行。指定返回值,在执行结束后返回result

        <T>Future<T> submit(Callable<T> task):将一个Callable对象提交给指定线程池,在有空闲线程时执行,返回Future

 

    ScheduledExecutorService代表可在指定延迟后或者周期性执行线程任务的线程池。

        ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit):指定Callable任务在delay延时后执行

        ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit):指定command任务在delay延时后执行

        ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):指定command任务在delay延时后执行,以设定频率重复执行

                执行:initialDelay initialDelay+peroid initialDelay+2 * peroid ....

        ScheduledFuture<?> scheduleAtFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):创建并执行一个在给定初始延迟后首次启用的定期操作,随后一次执行结束下次开始前都存在给定延时

 

包装线程不安全的集合:

    使用Collections提供的类方法可以把线程不安全的集合包装为线程安全

    <T> Collection<T> synchronizedCollections(Collections<T> c): 返回指定集合对应的线程安全的集合

    static <T> List<T> synchronizedList(List<T> list): 返回指定list对象对应的线程安全的list对象

    static <K,V> Map<K,V> synchronizedMap(Map<K,V> map): 返回指定Map对象对应的线程安全的Map对象

    static <T> Set<T> synchronizedSet(Set<T> set): 返回指定Set对象对应的线程安全的Set对象

    static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m): 返回指定SortedMap对象对应的线程安全的SortedMap对象

    static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s): 返回指定SortedSet对象对应的线程安全的SortedSet对象

 

    eg:HashMap m = Collections.synchronizedMap(new HashMap());  //把一个普通的HashMap包装为线程安全的HashMap对象

 

线程安全的集合类:

    分为两类:

    Concurrent开头的:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque

    CopyOnWrite开头的:CopyOnWriteArrayList、CopyOnWriteArraySet

 

    Concurrent开头的表示支持并发访问的集合,可以支持多线程并发写入访问,并且都是安全的,但读取操作不必锁定;CopyOnWrite开头的底层通过复制实现,当对它操作时,实际是对数组副本进行操作,因此线程安全。CopyOnWrite由于写入需要频繁复制数组,性能比较差,但是在读操作时,不需加锁,操作很快很安全,适合读取操作远远多于写入操作的情况,比如缓存。

    

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值