黑马程序员——06多线程的实现方式与安全机制
2014-06-27 15:18
211 查看
------- android培训、java培训、期待与您交流! ----------
1.多线程的两种实现方式:
Thread有多个重构造方法,其中有两种最为常用,两种实现方式就是利用的这两个构造函数
1.1继承方式
1.定义一个新类C,并继承Thread类
2.在C中复写run方法
3.实例化一个C类对象,然后调用start方法,线程在运行完毕后会自动销毁。
这里详细介绍下c.run()和c.start()的区别(c是创建好的一个线程):
c.run()就是调用复写的run方法,跟普通的调用方法一样
c.start()是开启线程并执行run()方法。
1.2继承方式
1.定义一个新类C,并实现Runnable接口,复写run方法
2.实例化一个C对象p
3.实例化一个Thread对象,采用的是第二种构造方法,所以要将p作为参数传进去
4.调用start();
建议使用继承方式,以为Java只支持但继承,但支持多实现
2.线程的5种状态,主要是靠是否持有执行资格和执行权来分辨的
新建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop() 或者自动销毁
3.“锁”的由来
没有锁的多线程是不安全的,举个例子:
4个窗口同时卖票,买票之前要设定好条件:票数大于0,当票数为1时,窗口1判断条件成立后就开始卖票,
在还未卖出之前窗口2也判断该条件,发现成立,然后俩个窗口都卖票,但是只剩下一张票了,程序就可能
卖出负数张票。
问题的原因:
当多条语句在操作同一个线程共享的数据时,一个线程对多条语句只执行了一部分,另一个线程就将操作权夺走
并执行语句,导致了共享数据的错误。
解决方法:
当一个线程执行多条语句时,在他未执行完时,其他线程不准执行,等于在线程进入一个房间操作时就在门上
挂把锁,操作完后再把锁打开。
3.1 挂“锁”的三种方式
挂锁的关键在于找准共享的数据,要把涉及到共享数据的代码锁起来,有三种方式:
1.同步代码块:
synchronized(obj)
{
//obj 表示同步监视器,是同一个同步对象
/**.....
TODO SOMETHING
*/
2.同步方法
格式:
在方法上加上synchronized 修饰符即可。 (一般不直接在 run 方法内,而是在它里面调用! )
synchronized 返回值类型 方法名(参数列表)
4000
{
/**.....
TODO SOMETHING
*/
}
同步方法的同步监听器其实的是 this
3.同步锁
jkd1.5 后的另一种同步机制:
通过显示定义同步锁对象来实现同步,这种机制,同步锁应该使用 Lock 对象充当。
在实现线程安全控制中,通常使用 ReentrantLock(可重入锁)。使用该对象可以显示地加锁和
解锁。
具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,
但功能更强大。
[java] view
plaincopy
public class X
{
//为了保证锁得唯一性,把锁的定义放在共享数据操作代码的外部,共享数据用try包裹
private final ReentrantLock lock = new ReentrantLock();
//定义需要保证线程安全的方法
public void m(){
//加锁
lock.lock();
try{
//... method body
}
finally{
//在 finally 释放锁
lock.unlock();
}
}
}
3.2死锁
死锁产生的原因是两把锁互相嵌套,两个线程各持自己的锁不放。
[java] view
plaincopy
public class sisuo {
public static void main(String[] args) {
Object A = new Object ();
Object B = new Object ();
Thread thread1 = new Thread(new sisuos(true));
Thread thread2 = new Thread(new sisuos(false));
thread1.start();
thread2.start();
}
}
[java] view
plaincopy
<p>public class sisuos implements Runnable
{</p><p> private boolean flag ;
sisuos(boolean flag)
{
this.flag = flag;
}
public void run ()
{
while(true){
if(flag)
{
synchronized (suo.A) {
System.out.println(" 我有A锁");
synchronized (suo.B) {
System.out.println("我有AB锁");
}
}
}
else {
synchronized (suo.B) {
System.out.println(" 我有B锁");
synchronized (suo.A) {
System.out.println("我有AB锁");
}
}
}
}
}
}</p>
[java] view
plaincopy
<p>public class suo {</p><p> public static Object A = new Object ();
public static Object B = new Object ();
}</p>
4.线程间通讯
线程间通讯其实就是多个线程共同操作同一个共享资源,但是操作动作不同。
也就是多个实现了Runnabl接口不同的类,他们有不同的run方法
那么怎样保证他们是操作的是同一个共享资源呢?
1.构造方法从外部接受同一个资源
2.单例
4.1等待唤醒机制
为了使2个线程轮流操作,需要加入等待唤醒机制,他需要一个标志while(flag )
等待的方法:
锁.wait()
唤醒的方法
锁.notify()
需要注意的的是wait和notify的对象必须一致
但这样容易导致死锁(全部wait),所以用notifyAll
使用同步锁之后有另一种等待唤醒方法(推荐使用)
它提供了另一种监视器Condition,
set(wait-set)。其中,
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
他最大的好处在于可以定义多个监视器。
[java] view
plaincopy
//多线程的共享资源,要求有生产功能和销售功能,而且生产一个销售一个
public class Resource {
private boolean isHas = false;
private int count = 0;
ReentrantLock lock = new ReentrantLock();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition con_pro = lock.newCondition();
Condition con_sale = lock.newCondition();
public void product()
{
lock.lock();
try
{
while(isHas)
try {con_pro.await();} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+ "生产了商品"+ (++count));
isHas = true;
con_sale.signal();
}
finally
{
lock.unlock();
}
}
public void Sale ()
{
lock.lock();
try
{
while(!isHas)
try {con_sale.await();} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"出售了商品"+count);
isHas = false;
con_pro.signal();
}
finally
{
lock.unlock();
}
}
}
[java] view
plaincopy
//生产线程
public class Productor implements Runnable
{
private Resource resource ;
public Productor(Resource resource) {
this.resource = resource;
}
public void run()
{
while(true)
{
resource.product();
}
}}
[java] view
plaincopy
//销售线程
public class Customer implements Runnable
{
private Resource resource ;
public Customer(Resource resource) {
this.resource = resource;
}
public void run()
{
while(true)
{
resource.Sale();
}
}
}
[java] view
plaincopy
<p>public class sisuo {
public static void main(String[] args) {
Resource resource = new Resource();</p><p> //构造两个生产线程,两个销售线程
new Thread (new Productor(resource)).start();
new Thread (new Productor(resource)).start();
new Thread (new Customer(resource)).start();
new Thread (new Customer(resource)).start();
}
}</p>
------- android培训、java培训、期待与您交流! ----------
1.多线程的两种实现方式:
Thread有多个重构造方法,其中有两种最为常用,两种实现方式就是利用的这两个构造函数
Thread() 分配新的 Thread对象。 |
Thread(Runnable target) 分配新的 Thread对象。 |
1.定义一个新类C,并继承Thread类
2.在C中复写run方法
3.实例化一个C类对象,然后调用start方法,线程在运行完毕后会自动销毁。
这里详细介绍下c.run()和c.start()的区别(c是创建好的一个线程):
c.run()就是调用复写的run方法,跟普通的调用方法一样
c.start()是开启线程并执行run()方法。
1.2继承方式
1.定义一个新类C,并实现Runnable接口,复写run方法
2.实例化一个C对象p
3.实例化一个Thread对象,采用的是第二种构造方法,所以要将p作为参数传进去
4.调用start();
建议使用继承方式,以为Java只支持但继承,但支持多实现
2.线程的5种状态,主要是靠是否持有执行资格和执行权来分辨的
新建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop() 或者自动销毁
3.“锁”的由来
没有锁的多线程是不安全的,举个例子:
4个窗口同时卖票,买票之前要设定好条件:票数大于0,当票数为1时,窗口1判断条件成立后就开始卖票,
在还未卖出之前窗口2也判断该条件,发现成立,然后俩个窗口都卖票,但是只剩下一张票了,程序就可能
卖出负数张票。
问题的原因:
当多条语句在操作同一个线程共享的数据时,一个线程对多条语句只执行了一部分,另一个线程就将操作权夺走
并执行语句,导致了共享数据的错误。
解决方法:
当一个线程执行多条语句时,在他未执行完时,其他线程不准执行,等于在线程进入一个房间操作时就在门上
挂把锁,操作完后再把锁打开。
3.1 挂“锁”的三种方式
挂锁的关键在于找准共享的数据,要把涉及到共享数据的代码锁起来,有三种方式:
1.同步代码块:
synchronized(obj)
{
//obj 表示同步监视器,是同一个同步对象
/**.....
TODO SOMETHING
*/
2.同步方法
格式:
在方法上加上synchronized 修饰符即可。 (一般不直接在 run 方法内,而是在它里面调用! )
synchronized 返回值类型 方法名(参数列表)
4000
{
/**.....
TODO SOMETHING
*/
}
同步方法的同步监听器其实的是 this
3.同步锁
jkd1.5 后的另一种同步机制:
通过显示定义同步锁对象来实现同步,这种机制,同步锁应该使用 Lock 对象充当。
在实现线程安全控制中,通常使用 ReentrantLock(可重入锁)。使用该对象可以显示地加锁和
解锁。
具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,
但功能更强大。
[java] view
plaincopy
public class X
{
//为了保证锁得唯一性,把锁的定义放在共享数据操作代码的外部,共享数据用try包裹
private final ReentrantLock lock = new ReentrantLock();
//定义需要保证线程安全的方法
public void m(){
//加锁
lock.lock();
try{
//... method body
}
finally{
//在 finally 释放锁
lock.unlock();
}
}
}
3.2死锁
死锁产生的原因是两把锁互相嵌套,两个线程各持自己的锁不放。
[java] view
plaincopy
public class sisuo {
public static void main(String[] args) {
Object A = new Object ();
Object B = new Object ();
Thread thread1 = new Thread(new sisuos(true));
Thread thread2 = new Thread(new sisuos(false));
thread1.start();
thread2.start();
}
}
[java] view
plaincopy
<p>public class sisuos implements Runnable
{</p><p> private boolean flag ;
sisuos(boolean flag)
{
this.flag = flag;
}
public void run ()
{
while(true){
if(flag)
{
synchronized (suo.A) {
System.out.println(" 我有A锁");
synchronized (suo.B) {
System.out.println("我有AB锁");
}
}
}
else {
synchronized (suo.B) {
System.out.println(" 我有B锁");
synchronized (suo.A) {
System.out.println("我有AB锁");
}
}
}
}
}
}</p>
[java] view
plaincopy
<p>public class suo {</p><p> public static Object A = new Object ();
public static Object B = new Object ();
}</p>
4.线程间通讯
线程间通讯其实就是多个线程共同操作同一个共享资源,但是操作动作不同。
也就是多个实现了Runnabl接口不同的类,他们有不同的run方法
那么怎样保证他们是操作的是同一个共享资源呢?
1.构造方法从外部接受同一个资源
2.单例
4.1等待唤醒机制
为了使2个线程轮流操作,需要加入等待唤醒机制,他需要一个标志while(flag )
等待的方法:
锁.wait()
唤醒的方法
锁.notify()
需要注意的的是wait和notify的对象必须一致
但这样容易导致死锁(全部wait),所以用notifyAll
使用同步锁之后有另一种等待唤醒方法(推荐使用)
它提供了另一种监视器Condition,
Condition将
Object监视器方法(
wait、
notify和
notifyAll)分解成截然不同的对象,以便通过将这些对象与任意
Lock实现组合使用,为每个对象提供多个等待
set(wait-set)。其中,
Lock替代了
synchronized方法和语句的使用,
Condition替代了 Object 监视器方法的使用。
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
他最大的好处在于可以定义多个监视器。
[java] view
plaincopy
//多线程的共享资源,要求有生产功能和销售功能,而且生产一个销售一个
public class Resource {
private boolean isHas = false;
private int count = 0;
ReentrantLock lock = new ReentrantLock();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition con_pro = lock.newCondition();
Condition con_sale = lock.newCondition();
public void product()
{
lock.lock();
try
{
while(isHas)
try {con_pro.await();} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+ "生产了商品"+ (++count));
isHas = true;
con_sale.signal();
}
finally
{
lock.unlock();
}
}
public void Sale ()
{
lock.lock();
try
{
while(!isHas)
try {con_sale.await();} catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"出售了商品"+count);
isHas = false;
con_pro.signal();
}
finally
{
lock.unlock();
}
}
}
[java] view
plaincopy
//生产线程
public class Productor implements Runnable
{
private Resource resource ;
public Productor(Resource resource) {
this.resource = resource;
}
public void run()
{
while(true)
{
resource.product();
}
}}
[java] view
plaincopy
//销售线程
public class Customer implements Runnable
{
private Resource resource ;
public Customer(Resource resource) {
this.resource = resource;
}
public void run()
{
while(true)
{
resource.Sale();
}
}
}
[java] view
plaincopy
<p>public class sisuo {
public static void main(String[] args) {
Resource resource = new Resource();</p><p> //构造两个生产线程,两个销售线程
new Thread (new Productor(resource)).start();
new Thread (new Productor(resource)).start();
new Thread (new Customer(resource)).start();
new Thread (new Customer(resource)).start();
}
}</p>
------- android培训、java培训、期待与您交流! ----------
相关文章推荐
- 带有安全机制的axis2实现方式(三)
- 关于Session的机制,实现方式和安全、单点故障问题
- 带有安全机制的axis2实现方式(二)
- 关于Session的机制,实现方式和安全、单点故障问题
- Exception C++中讨论的异常安全栈,封装内存管理的版本,使用"成员变量"的方式实现
- C++异常机制的实现方式和开销分析
- C++异常机制的实现方式和开销分析(转载)
- Java事件处理机制 - 事件监听器的四种实现方式
- Java事件处理机制-事件监听器的实现方式
- python中package机制的两种实现方式
- python中package机制的两种实现方式
- [转] C++异常机制的实现方式和开销分析
- 文档安全加密系统的实现方式
- 802.11基本传输方式-信道竞争机制及QoS的实现机制
- python中package机制的两种实现方式(转载)
- C++异常机制的实现方式和开销分析
- C++异常机制的实现方式和开销分析
- 如何实现基于消息/传输安全验证机制下的windows身份验证过程、无任何验证
- [转]C++异常机制的实现方式和开销分析
- C++异常机制的实现方式和开销分析(作者:白杨)