java多线程基础知识1

重要的包:java.util.concurrent
多线程中关于线程任务的定义需要明确如下三个东西:Thread(类),Runnable(interface),Callable(interface)

1.ThreadLocal

    顾名思义它是local variable(线程局部变量)。它的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量
    本质上,每个当前运行的Thread都会持有一个Map,ThreadLocal类对这个Map的访问进行了封装,因此在线程中可以把一个新生成的对象通过ThreadLocal放入这个Map,这样可以保证该线程在以后每次从ThreadLocal对象即这个Map中取得的对象都只是在该线程中可用,不会被其它线程访问到。

使用场景:

  To keep state with a thread (user-id, transaction-id, logging-id)
  To cache objects which you need frequently

  隐式传参


注意:使用ThreadLocal,一般都是声明在静态变量中,如果不断的创建ThreadLocal而且没有调用其remove方法,将会导致内存泄露。
ThreadLocal<T>

initialValue() : T // 返回ThreadLocal变量的初始值。在一个线程中,如果在调用set()之前调用get()时候,initialValue()  就会被调用,而且也只会被调用一次。如果在调  用get()之前已经调用过set()了,那么initialValue()就不会被调用。

get() : T          //得到这个ThreadLocal变量的值(对于当前线程的)。
set(T value)   //设置这个ThreadLocal的变量的值(对于当前线程的)。

remove()

例子:
public class MyThread extends Thread {


    @SuppressWarnings("rawtypes")
    public static ThreadLocal tl = new ThreadLocal();

    @SuppressWarnings("unchecked")
    public void run() {
         tl.set(new Client(20));
        
         Client c= (Client) tl.get();
         int r = new Random().nextInt(100);
         System.out.println(r);
         c.clNum=c.clNum+r;
         tl.set(c);
         System.out.println("Thread " + Thread.currentThread().getName() +
                  " has client " +tl.get());
        
    }

    private class Client {
        private int clNum;

        Client(int n) {
            clNum = n;
        }

        public String toString() {
            return "Client[" + clNum + "]";
        }
    }

}

public class ThreadLocalTest{

    /**
     * @param args
     */
    public static void main(String[] args) {
        Thread t = new MyThread();
        Thread t1 = new MyThread();
        t.start();
        t1.start();
   }
}


2.任务:

 2.1 Runnable

             众所周知,省略

 2.2 Callable:需要返回值的任务

   

ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Object> task = new Callable<Object>() {
public Object call() throws Exception {
Object result = "...";
return result;
}
};
Future<Object> future = executor.submit(task);
future.get();


Future<Object> future = executor.submit(task);
// 等待到任务被执行完毕返回结果
// 如果任务执行出错,这里会抛ExecutionException
future.get();
//等待3秒,超时后会抛TimeoutException
future.get(3, TimeUnit.SECONDS);



3.阻塞队列:

ArrayBlockingQueue
LinkedBlockingQueue

SynchronousQueue


使用BlockingQueue的时候,尽量不要使用从Queue继承下来的方法,否则就失去了Blocking的特性了。
在BlockingQueue中,要使用put和take,而非offer和poll。如果要使用offer和poll,也是要使用带等待时间参数的offer和poll。
使用drainTo批量获得其中的内容,能够减少锁的次数
BlockingQueue<E>:
put(E)
take() : E
offer(E, long, TimeUnit) : boolean
poll(long, TimeUnit) : E
remainingCapacity()
drainTo(Collection<? super E>) : int
drainTo(Collection<? super E>, int ) : int

正确用法:
final BlockingQueue<Object> blockingQ = new ArrayBlockingQueue<Object>(10);
Thread thread = new Thread("consumer thread") {
public void run() {
for (;;) {
try {
Object object = blockingQ.take(); // 等到有数据才继续
handle(object);
} catch (InterruptedException e) {
break;
} catch (Exception e) {
// handle exception
}
}
}
};


4.锁

    4.1ReentrantLock和Synchronized

        Synchronized是Lock的一种简化实现,一个Lock可以对应多个Condition,而synchronized把Lock和Condition合并了,一个synchronized Lock只对应一个Condition,可以说Synchronized是Lock的简化版本。在JDK 5,Synchronized要比Lock慢很多,但是在JDK 6中,它们的效率差不多。
        重入锁(ReentrantLock)是一种递归无阻塞的同步机制

    4.2 使用AtomicInteger

class Counter {
private volatile int count = 0;
//若要线程安全执行执行count++,需要加锁
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}

class Counter {
private AtomicInteger count = new AtomicInteger();
//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}

java.util.concurrent中实现的原子操作类包括:
AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference


5.使用Lock-Free算法

class Counter {
private volatile int max = 0;
//若要线程安全,需要加锁
public synchronized void set(int value) {
if (value > max) {
max = value;
}
}
public int getMax() {
return max;
}
}

class Counter {
private AtomicInteger max = new AtomicInteger();
public void set(int value) {
//LockFree算法,不需要加锁。
//通常都是三个部分组成:
//1 循环
//2 CAS (CompareAndSet)
//3 回退

//循环
for (;;) {
int current = max.get();
if (value > current) {
//CAS
if (max.compareAndSet(current, value)) {
break;
} else {
//回退
continue;

}
} else { break; }
}
}
public int getMax() {
return max.get();
}

}


<span style="font-size:18px;">对比如下两种线程安全实现
class BeanManager {
private Map<String, Object> map = new HashMap<String, Object>();
public Object getBean(String key) {
synchronized (map) {
Object bean = map.get(key);
if (bean == null) {
map.put(key, createBean());
bean = map.get(key);
}
return bean;
}
}
}

class BeanManager {
private ConcurrentMap<String, Object> map = new ConcurrentHashMap<String, Object>();
public Object getBean(String key) {
Object bean = map.get(key);
if (bean == null) {
//使用ConcurrentMap,避免直接使用锁,锁由数据结构来管理。
map.putIfAbsent(key, createBean());
bean = map.get(key);
}
return bean;
}
}</span>
ConcurrentHashMap并没有实现Lock-Free,只是使用了分离锁的办
法使得能够支持多个Writer并发。 ConcurrentHashMap需要使用更
多的内存。


6.使用CopyOnWriteArrayList

对比如下两种实现:
class Engine {
private List<Listener> listeners = new ArrayList<Listener>();
public boolean addListener(Listener listener) {
synchronized (listeners) {
return listeners.add(listener);
}
}
public void doXXX() {
synchronized (listeners) {
for (Listener listener : listeners) {
listener.handle();
}
}
}
}

适当使用CopyOnWriteArrayList,能够提高
读操作时的效率。

class Engine {
private List<Listener> listeners = new CopyOnWriteArrayList <Listener>();
public boolean addListener(Listener listener) {
return listeners.add(listener);
}
public void doXXX() {
for (Listener listener : listeners) {
listener.handle();
}
}
}


使用支持CAS的数据结构,避免使用锁,如:

dAtomicXXX、ConcurrentMap、CopyOnWriteList、ConcurrentLinkedQueue


7.CAS:Compare and Swap, 比较并交换。

    CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
while(true){
   if(A==V){
      V=B;
      return;
   }
   else{
      A==V;
   }
}

public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}


8.线程控制:

  8.1.CountDownLatch

  等待多个线程都结束,再执行后续代码
  关键方法:
  final CountDownLatch compeletTask = new CountDownLatch(COUNT):初始化,和并发数相等
  CountDownLatch.countDowm():该方法实现计数功能,完成一个线程任务,数据减1
  CountDownLatch.await():等待数据归0
 例子: 
final int COUNT=10;
  final CountDownLatch compeletTask = new CountDownLatch(COUNT);
  for (int i = 0; i < COUNT; i++) {
    Thread thread = new Thread("workor"+i){
        public void run(){
            long time = 5*1000;
            try {
                Thread.sleep(time);
                System.out.println(Thread.currentThread().getName()+"---execute-----");
                compeletTask.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };
    thread.start();
  }
  compeletTask.await();
  System.out.println("-----finish------");

或者當你啓動很多線程时,希望所有线程等到通知后才去执行:
final CountDownLatch compeletTask = new CountDownLatch(1);
for (int i = 0; i < 10; i++) {
    Thread thread = new Thread("workor"+i){
        public void run(){
            try {
                compeletTask.await();//等待通知
                                //do something
                Thread.sleep(3000);
                System.out.println(Thread.currentThread().getName()+"---execute-----");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
                    
        }
    };
            thread.start();
}
compeletTask.countDown(); //通知


 8.2.CyclicBarrier

    这个类可以这么理解,这是一个对多线程进行协调的类,当启动多个线程后,等待多个线程都完成各自的任务后,最后到达barrier的线程,然后去执行barrier中定义的任务

  例子 
public class PerformaceTest {

    private int threadCount;
    private CyclicBarrier barrier;
    private int loopCount = 10;

    public PerformaceTest(int threadCount){
        this.threadCount=threadCount;
                //参数threadCount 定义的是多少个线程任务到达barrier后,去执行Runnable中的任务,
                //如果此处的threadCount比并发的线程数大,那么就不会执行这个任务
                //通过日志发现,该任务的执行是最后一个达到barrier的线程执行这个Runnable
        barrier = new CyclicBarrier(threadCount, new Runnable() {
            
            @Override
            public void run() {
                collectTestResult();                
            }
        });
        for (int i = 0; i <threadCount; i++) {
            Thread thread = new Thread("test-thread "+i){
                public void run(){
                       for (int j = 0; j <loopCount; j++) {
                            doTest();
                       }

                       try { 
                    	   barrier.await(); 
                       } catch (InterruptedException e) { 
                    	   // TODO Auto-generated catch block 
                    	   e.printStackTrace(); 
                       } catch (BrokenBarrierException e) { // TODO Auto-generated catch block 
                    	   e.printStackTrace(); 
                       } 
                } 
             };
             thread.start();
        } 
    }
    private void collectTestResult() { 
    	System.out.println(Thread.currentThread().getName()+" collectTestResult");
    } 
    private void doTest(){ 
    	try { 
    		Thread.sleep(3000); 
    	} catch (InterruptedException e) { // TODO Auto-generated catch block 
    		e.printStackTrace(); 
    	} 
    	System.out.println(Thread.currentThread().getName()+" doTest"); 
    } 
    
    public static void main(String[] args) {
    	new PerformaceTest(5); 
    } 
}



9.定时任务

备注:有了ScheduledExecutorService,不建议你再使用java.util.Timer,因为它无论功能性能都不如ScheduledExecutorService。ScheduledExecutorService接口:schedule(Runnable command, long delay, TimeUnit unit) : ScheduledFutureschedule(Callable<V> callable, long delay, TimeUnit unit) : ScheduledFuturescheduleAtFixedRate(Runnable comand, long initDelay, long period, TimeUnit unit) : ScheduledFuturescheduleWithFixedDelay(Runnable command, long initDelay, long delay, TimeUnit unit) : ScheduledFuture

例子

public class ScheduledExecutorServiceTest {

    public static void main(String[] args) {
        ScheduledExecutorService ss = Executors.newScheduledThreadPool(2);

        Thread thread1 = new Thread("thead1") {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" *****");

            }
        };
        Thread thread2 = new Thread("thead2") {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" --------");

            }
        };
        Thread thread3 = new Thread("thead3") {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+" &&&&&");

            }
        };

        ScheduledFuture<?> future = ss.scheduleAtFixedRate(thread1, 2, 2, TimeUnit.SECONDS);
        ScheduledFuture<?> future1 = ss.scheduleAtFixedRate(thread2, 2, 2, TimeUnit.SECONDS);
        ScheduledFuture<?> future2 = ss.scheduleAtFixedRate(thread3, 2, 2, TimeUnit.SECONDS);
        

    }
}

9.大规模定时器TimerWheel

    存在一种算法 TimerWheel ,适用于大规模的定时器实现。这个 算法最早是被设计用来实
现 BSD 内核中定时器的,后来被广泛 移植到诸如 ACE 等框架中,堪称 BSD 中经典算法之
一,能针 对定时器的各类常见操作提供接近常数时间的响应,且能根据需 要很容易进行
扩展


          



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值