Java中的多线程编程

来源:互联网 发布:淘宝购物车分开地址 编辑:程序博客网 时间:2024/06/10 07:36

一、线程安全

安全包括两个含义 1)互斥 即某些方法或者代码块只能顺序执行,不能多个线程并发执行;2)可见 对于共享变量,一个线程的修改必须对另外的线程可见

1、java.util.concurrency.atomic包中包含了原子变量类(atomic variable class),这些类用来实现数字和对象引用的原子状态转换。例如,AtomicLong类可以确保所有对该类的对象的操作都是原子的。

2、即使将类中每个状态变量都用原子变量类实现,仍有可能出现问题。如果var a和var b有关系(比如b是a的第一个因子),那么即使对a和b的访问都是原子的(由原子变量类保证),这个类仍然不是线程安全的。

例如,a和b都是数值,而且b是a的2倍。现在有两个线程t1和t2。它们分别获得了a的值。现在t1修改了a的值,之后t2获取的b的值,那么t2获得的值就不在满足b=2*a

的关系。因此,要在单一的原子操作中实现相关状态变量的更新。

3、内部锁(synchronized块)由synchronized声明的程序块(语句组或者方法)可以保证在任意时刻,只有1个或者0个线程在此程序块内部。而且这个内部锁,就是对象(static的方法是类)。当有某个线程在synchronized内部时,其他试图进入的线程都会挂起。

要注意synchronized是可重进的。可重进的含义是,当线程t1在某个synchronized中时,如果t1再次请求这个锁,可以直接获得锁而不必挂起。

从实现的角度说,synchronized为每个锁关联一个计数器和占有线程。初始计数器为0,没有占有线程。线程试图获得锁时,检查计数器是否为0。如果为0则获得锁,否则阻塞。当某个线程获得锁以后,就将计数器+1,把自己设置为占有线程;当相同的线程再次申请锁时,直接将计数器+1即可;当一个线程退出时,将计数器-1。直到计数器为0时,锁被释放。

下面的解释来自与《Java线程》第三版:“当一个method被声明为synchronized,要执行此method的thread必须先取得一个token,我们将它称之为lock。一旦该thread取得lock,他就运行此method然后释放掉(或者返回)此lock。不管method是怎样返回的(包括通过异常),此lock都会释放。每个对象只有一个lock,所以如果有两个不同的thread试着在同一时间调用统一对象的synchronized method,只有一个能够获得lock并立刻执行method,另外一个必须等到地一个thread是放掉lock后在能够运行method。”

即使有多个method声明为synchronized它们的行为也和上面类似,不可能在不同的thread中并行的运行。

4、static method没有this引用的概念,对于一个不存在的对象取得这个对象的lock是不可能的。对于static method的同步,可以引入class lock的概念。如同可以从各个instance去的lock一样,也可以从各个class取得lock。当一个synchronized的static method被调用时,thread会尝试取得此class的lock,机制与instance的lock相同。


总结起来,对于每一个class只有一个thread可以调用synchronized static method,对于每一instance,只有一个thread可以调用synchronized method。任意数目的thread可以执行一般的method。并且如果一个非static synchronized method调用了static synchronized method,它会去的两把锁。

5、lock interface

Java中有多个calss实现了这一接口。可以通过实例化这些类,然后用实例化的instance实现同步。之后的同步就可以通过调用lock.lock()和lock.unlock()实现。

private Lock lock=new Reent<pre name="code" class="java">package Lock;import java.util.concurrent.*;import java.util.concurrent.locks.*;public class LockerUse {/** * ReentrantLock实现了lock interface */private Lock locker = new ReentrantLock();public void  LockMethod1(){try{locker.lock();/** * do somrthing */}finally{locker.unlock();}}public void LockMethod2(){try{locker.lock();/** * do something */}finally{locker.unlock();}}}

在上面的代码中,thread调用LockMethod1或者LockMethod2时要获得lock,并且2个方法使用同一个lock,即任意时刻至多只能有一个thread调用LockMethod1和LockMethod2,。另外,将unlock方法放入finally中可以保证在出现异常的情况下仍然可以unlock。

eg:

public class Widget {public synchronized void doSomething(){}}public class LoggingWidget extends Widget{public synchronized void doSomething(){System.out.println("son doSomething");super.doSomething();}public static void main(String[] args){LoggingWidget lw=new LoggingWidget();lw.doSomething();}}
当main中调用LoggingWidget类对象的lw的doSomething方法时,要获得两把锁(LoggingWidget和Widget)才能调用doSomething(),而为了调用super.doSomething(),必须获得Widget的锁。如果synchronized不支持可重入的话,将会引起死锁。

4、用valotile保证可见性,valotile也只能保证可见性,而不能保证原子性

5、ThreadLocal

用ThreadLocal管理在多线程之间共享的数据。get方法可以保证获得的引用是执行ThreadLocal的线程采用set修改后的新值。

二、Java的规定

1、Java将(除了long与double)变量的基本加载与存储定义为atomic。这意味着变量的值在存储时不会有中间状态,变量也不会在加载到寄存器的中途被改变。

2、“使用synchronized时,取得同步化的lock代表所有暂时存储与寄存器的值都会被清空(flush)到主存”,用在变量的get和set上,可以保证变量在线程之间修改i的可见性。

3、Java指定对volatile变量的加载与存储都是atomic的,无论是不是double与long变量。如果变量被标记为volatile,每次使用该变量时值都必须从主存储器中读出,每次要写入该变量时,都必须写入主存储器。

4、Lock Interface

Lock Interface:

import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Condition;public interface LockInterface {void lock();void lockInterruptibly()throws InterruptedException;boolean tryLock();boolean tryLock(long time,TimeUnit unit)throws InterruptedException;void unlock();Condition newCondition();}
boolean tryLock()会尝试得到lock,如果成功得到lock会返回true;否则会返回false。可以通过返回值来让thread在得到lock和没有得到lock的情况下执行不同的task

boolean tryLock(long time,TimeUnit timeunit)与上面的区别就是在没有得到lock的情况下会等待指定的时间(数值+单位)。
5、只被synchronized method调用的method不需要再保护;因为调用这些方法的thread已经取得了lock。

6、上面说过支持synchronized支持可重入;支持可重入的锁机制成为“nested lock”;ReentrantLock支持nested lock。

7、ReentrantLock class

public class ReenTrantClass implements LockInterface{public int getHoldCount();public boolean isLocked();public boolen isHeldByCurrentThread();public int getQueueLength();}
int getHoldCount()返回当前thread对lock的需求量,返回值为0说明当前thread没有持有此lock;

boolean isLocked()返回lock instance是否被占有;

boolean isHeldByCurrentThread()返回此lock是否被当前thead持有;

int getQueueCount()返回等待获取此lock的thread数目。由于在计算这个值和返回之间存在race condition,因此这个值可能不准确。

0 0
原创粉丝点击