java并发编程一

基础知识

       线程的本质是机器执行的路径,是操作系统的最小执行单元,它完全是多任务操作系统之上的虚拟概念,从硬件角度来说每一个cup只有一条执行路径。成百上千个线程同时执行是操作系统调度的假象。

 

        每一个计算机程序,或每个进程,至少有一个线程,这个线程称为主线程,Java的主线程称为main thread。它以某个class类的main方法为起点。任何计算机程序都是由单一线程开始运行的。进程独享一个内存空间,进程中的线程共享这个内存空间,每个线程有单独的栈来存储数据。

 

并发编程的术语

        屏障(Barrier):屏障代表多个线程的集合点,需要所有的线程都到达该集合点后才能继续往下执行,因此先到达的线程会进入等待。

        条件变量(Condition Variable):条件变量是和某个锁(Lock)关联的变量,通常用于同步环境中,实现等待-唤醒机制。线程可以在拥有锁的情况下,等待某个条件变量;或者在拥有锁的前提下,唤醒一个或者全部正在等待某个条件变量的线程。

        条件变量也程事件变量(Event Variable)。

        临界区域(Critical Section):代表一个同步化的方法和代码块,所有线程必须串行的经过临界区域。本质上就是一个隐藏的锁获取和释放区域。

         锁(Lock):用于表示进入临界区域特定线程的访问权限。读写锁(Read/Write Lock)可以允许多个线程进行同时取得,只要它们同意都是进行“读”操作。

         监控器(Monitor): 该术语在不同的系统中代表的含义不同,可能代指Lock,也可能代指等待-唤醒机制

        互斥(Mutex):和Lock类似,它往往是跨进程的、基于操作系统级别的。

        信号量(Semaphore):线程可以等待一个或多个信号的计数,另一个线程则可以释放一个或多个信号的计数,当线程活的足够的计数后,停止等待。

 

 Java线程的创建和管理

 创建线程

 Java允许两种创建线程的风格

  1.   继承java.lang.Thread类,重写run方法
  2.   实现java.lang.Runable接口,并将起传给Thread的构造方法。

Thread对象本质上并不代表线程本身,而是一组与线程有关的方法和数据的封装。

Thread的构造函数有以下参数:

       name :   Thread的名称,默认Thread-N ,N是一个唯一的数字;

       Runable  target :新线程执行的指令列表,位于run()方法中

       ThreadGroup group : 新线程加入的线程组,默认与调用新线程构造方法的那个线程的线程组一致

       long  stackSize:新线程执行方法时,存放临时变量的栈大小。

 线程的生命周期方法

        new Thread() : 创建线程,在Java中,线程也是对象,因此它的对象是调用构造器完成的。线程被构造后即存在,但这是还没有进行执行,在这是其它线程可以和该线程进行交互,例如:设置优先级、线程守护和名称。

       start() : 启动线程,调用start()方法后,jvm中创建了一个新的线程,并且线程的isAlive()==true,等待系统调度进行执行。

      stop(): 终结线程,不能调用stop()方法来终结线程,它有缺陷,已经被弃用。

      sleep()/wait()/join()/suspend():线程等待, suspend()方法用来暂停线程,但是与stop()一样有缺陷,已经弃用。Thread.sleep()静态方法,可以让线程暂停一段时间,之后自动回复执行,sleep暂停时并不会释放锁。Object.wait()方法依赖于同步,不能单独进行使用,wait方法会释放对象持有的锁。

      notify()/notifyAll()/resume(): 线程恢复,notify()和notifyAll()用于唤醒调用wait()方法阻塞的线程,唤醒线程后,会尝试获取monitor。resume()用于恢复暂停的线程,有缺陷。

     清除:线程结束后,其Java对象扔可以访问,可以附带一些有价值的信息。如果Thread线程脱离作用域会被GC回收,回收可能带着系统资源的清理。

 

 线程的正确停止方式

设定标记位

 

public class MyThread implements Runnable {

    private volatile boolean flag = true;
    @Override
    public void run() {
        while ( flag )
            ;
        System.out.println( "Thread Interrupt" );
    }

    public static final void main( String[] arg ) throws Exception {
        MyThread myThread = new MyThread();
        Thread thread = new Thread( myThread );
        thread.start();
        System.out.println( "123" );
        TimeUnit.SECONDS.sleep( 5 );
        myThread.setFlag( false );
    }

    public void setFlag( boolean flag ) {
        this.flag = flag;
    }

    public boolean isFlag() {
        return flag;
    }
}
 利用中断

 调用该方法,可以让任何处于阻塞(Blocking)方法调用中的线程获得退出的机会。该方法有两个效应:

  1. 线程的阻塞方法(可能)会抛出InterruptedException,程序代码可以捕获此异常并退出
  2. 设置线程内部“已中断”标记为true,提示线程已经被中断。可以使用isInterrupted()方法来检查该标记。即使线程没有被Block,这个标记也会被设置
Thread t = new Thread() {
            public void run()
            {
                while ( !isInterrupted() )
                    ;
                System.out.println( "interrupted" );
            }
        };
        t.start();
        TimeUnit.SECONDS.sleep( 1 );
        t.interrupt();

 利用异常判断(针对出于阻塞状态的线程)

Thread t = new Thread() {
            public void run()
            {
                try
                {
                    TimeUnit.SECONDS.sleep( 3600 );
                }
                catch ( InterruptedException e )
                {
                    System.out.println( "interrupted" );
                }
            }
        };
        t.start();
        TimeUnit.SECONDS.sleep( 1 );
        t.interrupt();

    关于interrupt()方法,还需要注意:

  1. 如果线程在调用Object.wait()、join()、sleep()方法时被其它线程中断,其中断标记位会被清除,并接收到InterruptedException
  2. 如果线程在InterruptibleChannel上执行I/O时被其它线程中断,则通道被关闭,线程的中断标记位被设置,并接受到ClosedByInterruptException。 ServerSocketChannel、SocketChannel、FileChannel、DatagramChannel等都是可中断通道
  3. 如果线程在Selector上执行select操作时被中断,则线程的中断标记位被设置,并立即返回
 

猜你喜欢

转载自lianpeng0011.iteye.com/blog/2414929
今日推荐