并发

一、线程实现的方式

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()方法来访问该对象的内容。实质就是每个单独的线程都被分配了自己的存储,因为它们

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

 

posted @ 2017-03-10 11:31  羊皮纸月亮  阅读(217)  评论(0编辑  收藏  举报