Java并发编程(3)-- Thread

来源:互联网 发布:java编程思想 新手 编辑:程序博客网 时间:2024/06/06 02:41

1、Thread实现了Runnable接口,所以有两种线程类,一是继承Thread类,覆写run()方法,启动方式:xxThread xx=new xxThread();xx.start();;
二是实现runnable接口,实现run()方法,启动方式:Thread t = new Thread(new xxRunnable());t.start();。
推荐后者,将任务从线程中分离出来是比较好的设计,它将任务和任务执行分离开来了。

2、+isAlive():boolean,测试线程当前是否在运行。

3、+join():void,等待线程结束。
join方法的功能就是使异步执行的线程变成同步执行。
t1.start();
t1.join();
System.out.println(a);

4、+yield():void,使线程暂停并允许执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。
因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。
但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
在run()方法中使用 Thread.yield()。

5、synchronized 关键字,同步,一次只有一个线程可以访问
同步方法:public synchronized void deposit(){}
同步语句:synchronized (对象的引用){}

6、利用加锁同步
Lock lock = new ReentrantLock();创建一个锁
lock.lock();
//需要同步的代码
finally{
lock.unlock();//放在finally中确保会被执行
}

7、线程间协作

  Condition newDeposit = lock.newCondition();  lock.lock();  while(支出>现存){    newDeposit.await();//当前线程等待,直到发生某个条件  }  现存 - 支出  finally{ lock.unlock();}

…………………………………………………

 lock.lock();  现存++;  newDeposit.signalAll();唤醒所有等待线程  finally{ lock.unlock();}

条件由Lock对象创建,为了调用方法(await(),signal(),signalAll()),必须首先拥有锁。
这三个方法对应Java 5之前的wait(),notify(),notifyAll()—–Java的内置监听器 ,这些方法必须在同步方法或同步块中调用,当调用wait()方法时,它终止线程同时释放对象的锁。当线程被通知后,锁被重新自动获取。

8、生产者和消费者

两个条件的锁,notEmpty和notFull

 public void write(int a){    lock.lock();    try{        while(queue.size()==CAPACITY){            notFull.await();        }        queue.offer(a);        notEmpty.signal();    }catch(){}    finally{ lock.unlock(); }   }

…………………………………………

 public int read(){    int value=0;    lock.lock();    try{        while(queue.isEmpty){            notEmpty.await();        }        value=queue.remove();        notFull.signal();    }catch(){}    finally{        lock.unlock();        return value;    }   }

9、阻塞队列
在试图向一个满队列添加元素或者从空队列中删除元素时会导致线程阻塞。
可以用阻塞队列简化生产者和消费者例子,因为同步已在阻塞队列中实现。

10、信号量
信号量可以用来限制访问共享资源的线程数。在访问资源前,线程必须从信号量获取许可。
Semaphore semaphore = new Semaphore(int n);创建一个带指定数目许可的信号量
semaphore.acquire();获取许可,信号量中可用许可总量-1,如果无许可可用,线程就被锁住直到有可用许可为止。
semaphore.release();释放许可,信号量中可用许可总量+1。

11、死锁

synchronized (o1) {         synchronized (o2) {             }}

…………………………..

 synchronized (o2) {                synchronized (o1) {                 } }

使用一种名为 资源排序 的简单技术可以轻易地避免死锁的发生。

12、线程状态
线程有5种状态,新建、就绪、运行、阻塞或结束。
start()后线程进入就绪状态;run()后进入运行状态;join()、sleep()、wait()后进入阻塞状态;
yield()进入就绪状态;run执行完,线程结束。
isAlive()是用来判断线程状态的方法,就绪、运行和阻塞返回true;新建、结束返回false。

13、Java集合框架中的类不是线程安全的,Collections类提供六个静态方法来将集合转成同步版本。

14、线程同步方式

同步方法;

即有synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。

同步代码块;

即有synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。

使用特殊域变量(volatile)实现线程同步

a.volatile关键字为域变量的访问提供了一种免锁机制,b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,c.因此每次使用该域就要重新计算,而不是使用寄存器中的值,d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量 。

使用重入锁实现线程同步;

在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。

使用局部变量实现线程同步;

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

使用阻塞队列实现线程同步;

前面5种同步方式都是在底层实现的线程同步,但是我们在实际开发当中,应当尽量远离底层结构。 使用javaSE5.0版本中新增的java.util.concurrent包将有助于简化开发。LinkedBlockingQueue<E>是一个基于已连接节点的,范围任意的blocking queue。 队列是先进先出的顺序(FIFO)。

使用原子变量实现线程同步;

需要使用线程同步的根本原因在于对普通变量的操作不是原子的。那么什么是原子操作呢?原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作即-这几种行为要么同时完成,要么都不完成。在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,使用该类可以简化线程同步。其中AtomicInteger 表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),但不能用于替换Integer;可扩展Number,允许那些处理机遇数字类的工具和实用工具进行统一访问。
0 0