聊聊java线程

聊聊java线程

线程池

创建线程池

首先通过ExecutorService threadPool = Executors.newFixedThreadPool(thread);构建的线程池阿里java开发规范不提倡,其源码本质创建方式:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

手动创建线程池:

 private static int corePoolSize = 100;
    private static int maxPoolSize = 100;
    private static long keepAliveSeconds = 1L;
    private static int queueCapacity = 500;
    private static String ThreadNamePrefix = "thread-dataSet-job-%d";
    private static ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(ThreadNamePrefix).build();
    private static ExecutorService taskExecutorService = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveSeconds,
            TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(queueCapacity), threadFactory);

关闭线程池

这里聊一下线程池的关闭,shutdown()shutdownNow(),这两个关闭方式都会在执行完成后,isShutdown()的状态变成true。在并发线程中,shutdown()执行完后,会将剩下线程执行完,shutdownNow()会在执行完后,剩下的线程运行时会报错,这里需要在并发的时候特别注意,即注意shutdown()shutdownNow()在代码逻辑中存放的位置。

线程

线程的返回值

线程可以解决很多问题,比如之前文章的进程阻塞问题,还可以提高批量操作的执行效率等。
1、无返回值

public class FileCopyThread extends Thread {
    private String srcPath;
    private String destPath;
    boolean blockOK = true;

    public FileCopyThread(String srcPath, String destPath){
        this.srcPath = srcPath;
        this.destPath = destPath;
    }

    @Override
    public void run(){
        long beginTime = System.currentTimeMillis();
        Path start = Paths.get(srcPath);
        Path target = Paths.get(destPath);
        Path targetBlock = start;
        //todo 

        long endTime = System.currentTimeMillis();
        System.out.println("====destPath: "+ targetBlock  + ", cost time: " +
                (endTime-beginTime) + ", result: " + blockOK);
    }
}

使用方式:

executorService.submit(new FileCopyThread(srcMap.toString(), destMap.toString()));

2、有返回值

public class FileCopyThreadCallBack implements Callable{
    private String srcPath;
    private String destPath;
    boolean blockOK = true;

    public FileCopyThreadCallBack(String srcPath, String destPath){
        this.srcPath = srcPath;
        this.destPath = destPath;
    }

    @Override
    public Boolean call(){
        long beginTime = System.currentTimeMillis();
        Path start = Paths.get(srcPath);
        Path target = Paths.get(destPath);
        Path targetBlock = start;
        //todo

        long endTime = System.currentTimeMillis();
        System.out.println("====destPath: "+ targetBlock  + ", cost time: " +
                (endTime-beginTime) + ", result: " + blockOK);
        return blockOK;
    }
}

使用方式:

Future<Boolean> resultFuture = executorService.submit(
                            new CopyThreadCallback(srcMap.toString(), destMap.toString(), uuid));

有返回值,获取返回值结果时,如果在创建线程后直接获取结果resultFuture.get()这里当前线程执行完后,再往后继续执行下一个线程,造成结果是顺序执行,这里如果使用线程进行并发操作,需要注意resultFuture.get()的使用位置。

线程取消操作

这里介绍常见的线程取消函数cancel,例:

for (Future<Boolean> booleanFuture : futureList) {
                if (!booleanFuture.isDone()) {
                    booleanFuture.cancel(true);
                }
            }

在取消操作完之后,需要在代码中线程继续执行位置判断Thread.currentThread().isInterrupted()进行return跳出终止即可。

线程暂停与继续

有时候我们的业务开发会长时间消耗占用资源,暂停和继续可以帮助我们解决这类问题。
1、暂停方法:

/**
  * 暂停
  * 
  * @param uuid
  */
 public void suspend(String uuid) {
  uuid=true;
 }

2、继续方法

/**
  * 继续
  * 
  * @param uuid
  */
 public void resume(String uuid) {
  synchronized (uuid) {
   uuid=false;
   uuid.notifyAll();
  }
 }

这里针对的是uuid的暂停和继续。在用过程中调用方法即可。
3、函数暂停后,具体函数中判断暂停以及超时的例子如下:

boolean isEnd = false;
Boolean suspended = SuspendCacheUtil.get(uuid);
synchronized (uuid) {
    while (suspended != null && suspended) {
        log.info("====== Searching suspend ======" + uuid);
        try {
            uuid.wait(CbbMngConstants.WAITINGTIME);
            Long nowTime = System.currentTimeMillis();
            Long startTime = TimeCacheUtil.get(uuid);
            if (startTime != null && nowTime - startTime > CbbMngConstants.WAITINGTIME) {
                log.info("===== " + uuid + " ===== timeout end.");
                SuspendCacheUtil.set(uuid, false);
                isEnd = true;
                break;
            }
        } catch (InterruptedException e) {
            log.info("===== " + uuid + " ===== isInterrupted.");
            SuspendCacheUtil.set(uuid, false);
            isEnd = true;
            break;
        }
        suspended = SuspendCacheUtil.get(uuid);
    }
}
if (isEnd) {
    //todo
    return;
}

这里是代码片段,不能直接使用,为了助于了解逻辑,SuspendCacheUtil TimeCacheUtil为缓存函数。

线程锁

线程中非常重要的一个问题是要保证线程安全。其中synchronized和volatile关键字保证相关方法变量的安全,当然java中有些方法本身就是线程安全的。
volatile关键字保证了在多线程环境下,被修饰的变量在别修改后会马上同步到主存,这样该线程对这个变量的修改就是对所有其他线程可见的,其他线程能够马上读到这个修改后值。
例如:volatile并不能保证非源自性操作的多线程安全问题得到解决,volatile解决的是多线程间共享变量的可见性问题,而例如多线程的i++,++i,依然还是会存在多线程问题。
Java提供了java.util.concurrent.atomic 包来提供线程安全的基本类型包装类。即i++变成i.incrementAndGet();,需引入import java.util.concurrent.atomic.AtomicInteger;

synchronized

1、在多线程使用同一个对象的测试中,只允许同时使用一个对象锁,一个类锁。其中类锁,即类中静态方法和静态类。
1)静态类锁

public static synchronized void method3(String uuid)

2)对象锁

public synchronized void method1(String uuid)

其他操作(两个对象锁,两个类锁)搭配都互斥,只能等前一个线程解锁才能让下一个线程使用;
2、在对象分别new一个对象的情况下,允许同时使用任意的对象锁,也允许对象锁和一个类锁同时使用。
但是类锁不能够同时使用,会互斥,只能等前一个线程解锁才能让下一个线程使用。这里需要注意static加synchronized的方法的使用。
感兴趣的小伙伴可以做下测试,这里省略。

线程不安全方法举例

线程不安全会报错各种各样的问题,我们都知道List 线程不安全,其中的操作不是原子操作。Vector 是线程安全的,其中的操作有sync修饰,为同步操作。
在多线程使用List的过程中List<String> list = new ArrayList<>();对进行list进行addremoveremoveAll操作时会经常出现异常ArrayIndexOutOfBoundsException即数组越界,代码中看逻辑没有任何问题,原因在于List是线程不安全的。
在多线程中使用List时需要注意,按照如下方式使用List<String> list = Collections.synchronizedList(new ArrayList<>());多线程中使用非线程安全的方法需要特别注意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值