多线程核心知识

  • 线程通信

1.两个线程交替打印hello和world ,利用 synchronized + wait + notify  或者 lock   

synchronized :只有拿到对应的锁才能执行里面方法

wait: 当前线程阻塞,等待被唤醒,并且释放锁

notify: 唤醒被阻塞的线程

package com.basic.work.thread.通信;


/**
 * 
 * 
 * 这是线程通信的例子
 * @author chenzhubing
 *
 */
public class Test {
    
    public boolean flag = false;
    
    /**
     * @param args
     */
    public static void main(String[] args) {
        final Test test = new Test();
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    synchronized (test) {
                        try {
                            if(test.flag) test.wait();
                            System.out.println("hello");
                            test.flag = true;
                            test.notify();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
                
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    synchronized (test) {
                        try {
                            if(!test.flag) test.wait();
                            System.out.println("world");
                            test.flag = false;
                            test.notify();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    
                    
                }
                
            }
        });
        t1.start();
        t2.start();

    }

}

 

ReentrantLock:可重入锁,当前线程获取锁后,可以再次获取,增加了同步状态的值(计数器+1),在释放的过程中,不断减少同步状态,只为同步状态为0时,锁完全释放。
lock + condition.await + condition.signal 的原理和synchronized + wait + notify  类似,但是要注意lock需要手动上锁lock.lock()和释放锁lock.unlock()

 

package com.basic.work.thread.通信;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


/**
 * 
 * 这是线程通信的例子
 * @author chenzhubing
 *
 */
public class TestLock {
        
    static int status = 0;
    
    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();    
        final Condition condition1 = lock.newCondition();  
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0;i<10;i++){
                        try {
                            lock.lock();
                            if(status == 1){
                                condition1.await();
                            }
                            System.out.println("hello");
                            status = 1;
                            condition1.signal();
                        } catch (InterruptedException e) {
                        }finally{
                            lock.unlock();
                        }
                        
                    
                }
            }
        });
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0;i<10;i++){
                    try {
                        lock.lock();
                        if(status == 0){
                            condition1.await();
                        }
                        System.out.println("world");
                        status = 0;
                        condition1.signal();
                    } catch (InterruptedException e) {
                    }finally{
                        lock.unlock();
                    }
                    
                }
            }
        });
        
        t1.start();
        t2.start();
        
    }
}
  • ThreadLocal关键字

每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本。底层是维护了一个ThreadLocalMap,ThreadLocalMap中key存放的是当前线程

 Thread.currentThread(); value存放的是当前线程对应的变量。从而实现了线程间的数据隔离

package com.basic.work.thread.threadlocal;

public class SequenceNumber {
    private static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
        // 覆盖初始化方法  
        protected Integer initialValue() {  
              return 0;  
          }
    };
    public int getNetxNum(){
        local.set(local.get()+1);
        return local.get();
        
    }
    
    public static void main(String[] args) {
        SequenceNumber sn = new SequenceNumber();
        Thread t1 = new Thread(new ThreadLocalClient(sn));
        Thread t2 = new Thread(new ThreadLocalClient(sn));
        Thread t3 = new Thread(new ThreadLocalClient(sn));
        t1.start();
        t2.start();
        t3.start();
    }
    
}


 class ThreadLocalClient implements Runnable{
    private SequenceNumber seq;
    public ThreadLocalClient(SequenceNumber seq){
        this.seq = seq;
    }

    @Override
    public void run() {
        for(int i=0;i<3;i++){
            System.out.println("thread name="+Thread.currentThread().getName()+" sn="+seq.getNetxNum());
        }
    }
    
}

    
  • volatile关键字

保证可见性:当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

保证有序性:禁止进行指令重排序

不能保证原子性:在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。volatile

不能保证原子性,保证原子性需要用到Actomic(基于CAS)  或者锁。下面这个例子能说明volatile 不能保证原子性

package com.basic.work.thread.actomic;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Test {
    
    
    //volatile  具有可见性
    public static volatile int num = 0;
    
    //AtomicInteger 具有原子性
    public static AtomicInteger act = new AtomicInteger();
    
    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch latch = new CountDownLatch(10);
        
        for(int i = 0;i<10;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int j = 0;j<1000;j++){
                        num++;
                        act.incrementAndGet();
                    }
                    
                    latch.countDown();
                }
            }).start();
            
        }
        
        latch.await();
        System.out.println(num);
        System.out.println(act.intValue());
        
        
    }
}

输出结果act:10000,但是num的值不固定

 

  •  CountDownLatch关键字

主线程main,需要在A线程和B线程完成后,打印耗时 CountDownLatch latch = new CountDownLatch(2);  计数器count为2

子线程调用latch.countDown();  //count-- 主线程调用latch.await(); 这是个阻塞方法,count=0的时候才能执行

package com.basic.work.thread.countdownlatch;

import java.util.concurrent.CountDownLatch;

public class Test {
    
    
    public static void main(String[] args) throws InterruptedException {
        
        final CountDownLatch latch = new CountDownLatch(2);
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i =0;i<100;i++){
                    System.out.println("hello");
                }
                latch.countDown();
            }
        }).start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i =0;i<100;i++){
                    System.out.println("world");
                }
                latch.countDown();
            }
        }).start();
        
        latch.await();
        
        System.out.println("end..");
        
    }
    
    
}

 

 

 

 

 

posted @ 2019-12-04 14:47  兵哥无敌  阅读(223)  评论(0编辑  收藏  举报