多线程入门

274 阅读3分钟

线程是什么

程序执行的基本单位就是线程

基础概念

Java内存模型

要研究Java内存模型必须得先知道JVM运行时数据区结构

Java内存模型中定义了主存和工作内存之前变量(信息)的呼出和写入

顺序执行、并行、并发

  • 顺序执行指一个步骤一个步骤的往下执行(只有前面的步骤完成了才能执行后面的步骤)。 如:我们要洗澡,那么就要先脱衣服->洗澡;而不能同时脱衣服和洗澡
  • 并行执行指多个处理器同时执行(多个任务) 如:有多个人同时在洗澡
  • 并发执行指一个处理器同时处理多个任务(严格意义来说是顺序执行的,但是由于处理器执行较快所以给人感觉是并行执行的) 如:我们去看电影。我们看到是动画效果的,实际上存储的是每一个帧。由于切换速度比人眼感知快,所以看到是动画

为什么使用多线程?

1.切记不要为了多线程而多线程。使用多线程会让代码的执行逻辑变得复杂,所以使用需谨慎。
2.线程不是越多越好,一定要根据实际业务特征,按需索取。且必须考虑CPU和IO资源的使用情况

  • 合理的压榨CPU和内存资源
  • 提高应用的吞吐量
  • 降低应用的响应时间

多线程的评价标准是什么?

  • 安全性 如何处理共享变量的问题,使得每次都能获取到预期值
  • 生存性 无论发生什么该执行的逻辑都会被执行到(死锁)
  • 能快速大量的执行处理

线程状态及其转化关系

如何创建多线程

  • Thread
class PrimeThread extends Thread {
        final private String name;
        PrimeThread(String name) {
              this.name = name;
        }
 
        public void run() {
            System.out.println(name);
        }
    }

调用方式:
PrimeThread p = new PrimeThread("PrimeThread"); p.start();

  • Runnable
      class PrimeRun implements Runnable {
          final private String name;
          PrimeThread(String name) {
              this.name = name;
          }
 
          public void run() {
            System.out.println(name);
          }
      }

调用方式:
PrimeRun p = new PrimeRun(PrimeRun); new Thread(p).start();

  • Callable
    Callable是jdk1.5新增的功能。和Runnable功能类似,但是提供了调用返回值和检查性异常的功能
public class CallableTest implements Callable<String> {
    final private String name;
    public CallableTest(String name){
        this.name = name;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        final Future<String> future = Executors.newSingleThreadExecutor().submit(new CallableTest("CallableTest"));
        final String threadName = future.get(3000, TimeUnit.SECONDS);
        System.out.println(threadName);

        final Future<String> lambdaFuture = Executors.newCachedThreadPool().submit(() -> "你好");
        System.out.println(lambdaFuture.get(3000, TimeUnit.SECONDS));
    }

    @Override
    public String call() throws Exception {
        return name;
    }
}

补充说明

Thread实现了Runnable接口并扩展了很多方法

多线程安全问题

多线程环境下,如果对临界(共享)资源进行并发的读写、写写就会导致线程安全问题

  • volatile
    volatile保证了线程的可见性,即每次读取临界资源的时候都从主内存中呼出和回写,而不会从本地内存(寄存器)中获取
  • synchronized
    synchronized是一种同步锁。只有获取到锁的线程才有可能执行。synchronized可以修饰代码块、方法、静态方法、类文件 如:Collections.SynchronizedSortedSet、StringBuffer
  • lock
    读写锁,由于jdk1.5版本synchronized性能较低,所以产生了lock。但是jdk1.6之后对synchronized进行了优化。通过lock需要显性的获取锁和释放锁,而synchronized是jvm处理的
public final class SingletonLazy {
    private static SingletonLazy singletonLazy;
    private SingletonLazy(){}
    public static SingletonLazy getInstance(){
        if (null == singletonLazy){
            ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock().writeLock();
            try {
                if (lock.tryLock()){
                    if (null == singletonLazy){
                        singletonLazy = new SingletonLazy();
                    }
                }
            }finally {
                lock.unlock();
            }

//            synchronized (SingletonLazy.class){
//                if (null == singletonLazy){
//                    singletonLazy = new SingletonLazy();
//                }
//            }
        }
        return singletonLazy;
    }
}

Java关于多线程的类(java.util.concurrent)

  • Thread ThreadGroup
  • Runnable
  • Callable Future
  • Lock ReadWriteLock ReentrantReadWriteLock
  • ThreadFactory
  • Executor Executors(工具类)
  • Collections(集合类)
  • ...

附录

维基关于线程的解释 zh.wikipedia.org/wiki/线程
维基关于线程安全 zh.wikipedia.org/wiki/线程安全
线程状态及其转化关系 blog.csdn.net/houbin0912/…

声明

引用该文档请注明出处