欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

java 多线程,

来源: javaer 分享于  点击 39133 次 点评:56

java 多线程,


线程实现方式

通过调用线程的start方法,使线程进入就绪状态,等待CPU调度

1、继承类Thread,重写run方法

public class MyThread extends Thread{
   @Override
   public void run() {
      super.run();
      System.out.println(Thread.currentThread().getName());
}
调用
public class TestThread {
   @Test
   public void testThread() {
      MyThread myThread = new MyThread();
      myThread.setName("testThread");
      myThread.start();
}

2、实现接口Runable,重写run方法

public class MyRunable implements Runnable {
   @Override
   public void run() {
      System.out.println(Thread.currentThread().getName());
   }
}
@Test
public void testRunable() {
   Runnable runnable = new MyRunable();
   Thread thread = new Thread(runnable);
   thread.setName("testRunable");
   thread.start();
}

注意点:Thread类内部也实现了Runable接口

3、实现Callable接口,重写call方法;使用FutureTask对象包裹Callable的实现对象,以此来创建线程

public class MyCallable implements Callable<Integer> {
   private int i;
   @Override
   public Integer call() throws Exception {
      System.out.println(Thread.currentThread().getName());
      i++;
      return i;
   }
}
@Test
public void testCallable() {
   MyCallable myCallable = new MyCallable();
   FutureTask futureTask = new FutureTask<Integer>(myCallable);
   Thread thread = new Thread(futureTask);
   thread.setName("testCallable");
   thread.start();
   try {
      System.out.println(futureTask.get());
   } catch (InterruptedException e) {
      e.printStackTrace();
   } catch (ExecutionException e) {
      e.printStackTrace();
   }
}

注意点:FutureTask同时继承了Runable和Future接口,使其可以作为线程的创建目标,然后通过Future接口获取call的返回值;futureTask.get()方法时获取子线程的返回值,会一直阻塞,直至子线程返回。

线程异常

1、所有线程不允许抛出异常,需要内部自行处理,即使抛出异常父进程也不会处理,异常线程会自动结束不会影响其他线程;

2、如果想抛出异常则需要实现线程的异常处理类(uncaughtExceptionHandler)。

public class ThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
   @Override
   public void uncaughtException(Thread t, Throwable e) {
      System.out.println("捕获到异常:" + e.getMessage());
   }
}

线程加入异常处理器

@Test
public void testThread() {
   MyThread myThread = new MyThread();
   myThread.setName("testThread");
   myThread.setUncaughtExceptionHandler(new ThreadExceptionHandler());
   myThread.start();
}

线程方法

1、join,让一个线程等待另一个线程执行完成后再继续。

2、sleep,让一个线程休眠一定时间,并进入阻塞状态,等时间到了才继续执行。

3、yield,让一个线程进入就绪状态,CPU从就绪状态的队列中重新选择其他的线程执行,有可能是同一个。

4、wait,让一个线程暂停执行,直至有该对象调用notify/notifyAll才能唤醒线程。

线程同步方法

由于对共享资源的竞争,导致多线程之间出现错误。

1、synchronized关键字,可以修饰方法、修饰方法块,当修饰静态方法时,相当于锁类,和对象锁是两种不同类型的锁。

2、ReentrantLock锁,可以使的同步锁对象与共享资源解耦。

3、volatitle修饰符,修饰共享资源变量,每次访问或者请求该变量,都会重新获取最新的值,不是用寄存器中的值。

4、ThreadLocal类的使用,改变量是存储线程数据的线程存储类,它隔离了共享资源的争夺,让每个线程都保持有共享资源的一个副本,从而去除了竞争条件;经典的做法是针对线程不安全的SimpleDateFormmater。

注意点:其他同步机制考虑的是以时间换空间的做法,而ThreadLocal类采用空间换时间的做法;若是多个线程之间共享资源,已达到通信效果,则使用同步机制;若只是用来隔离线程关系则,使用ThreadLocal类。

@Override
public void run() {
   super.run();
   lock.lock();
   if (books.getPrices() >= 60) {
      System.out.println("消费前价格:" + books.getPrices());
      int price = books.getPrices() - 60;
      books.setPrices(price);
      System.out.println("消费后的价格:" + price);
   }
   lock.unlock();
}
@Test
public void testThreadPool() {
   Books books = new Books(1, 100);
   Lock lock = new ReentrantLock();
   ThreadPoolExecutor newThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
   MyThread myThread1 = new MyThread(books, lock);
   newThreadPoolExecutor.execute(myThread1);
   MyThread myThread2 = new MyThread(books, lock);
   newThreadPoolExecutor.execute(myThread2);
}

线程池

在多线程编程中,大部分时间会消耗在线程的创建和销毁,因此考虑是否能复用线程,从而引入了线程池。

1、线程池变量:corePoolSize,表示线程池的大小;maximumPoolSize,表示线程池任务量过大时,提供的补助策略,可以增加到多少个。

2、线程池提交任务的方法:submit(有返回值)、execute(无返回值),最终都是调用execute方法,submit只不过是封装了Future对象。

3、线程池处理策略:若线程池中的数目小于corePoolSize,则来一个任务创建一个线程去执行;如果大于等于corePoolSize,则会将任务存入缓存队列中,等待有空闲线程时再去执行;如果加入队列失败,则会重新创建一个线程去执行任务;如果线程数目达到了maximumPoolSize则会采取拒绝策略进行处理。

4、超时机制:当线程数目大于corePoolSize时,如果某个线程超过keepalivedTime,则线程会被终止,直到线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

5、线程初始化:默认情况下,创建线程池后是没有线程的,需要提交任务后才回去创建线程;初始化方法:prestartCoreThread()创建一个核心线程;prestartAllCoreThreads()创建所有线程。

6、任务缓存队列(workerQueue用来存放等待执行的任务):ArrayBlockingQueue(基于数组的先进先出)、LinkedBlockingQueue(基于链表的先进先出)、synchronousQueue(不会保存队列,直接创建一个新的线程去执行)。

7、缓存队列拒绝策略(线程池数量超过maximumPoolSize):ThreadPoolExecutor.AbortPolicy(丢弃任务,抛出拒绝异常)、ThreadPoolExecutor.DiscardPolicy(丢失任务,不抛出异常)、ThreadPoolExecutor.DiscardOldestPolicy(丢弃前面的任务)、ThreadPoolExecutor.CallerRunsPolicy(由线程调度处理该任务)。

8、线程创建:new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAlivedTime,TimeUnit,workQueue,threadFactory(可忽略),RejectedExecutionHandler (可忽略));推荐使用Executor的静态方法创建线程池:Executors.newFixedThreadPool(int);ForkJoinPool(jdk8引入)每个线程维护一个队列,适合有父子关系的任务,可用较小的线程数执行多数的任务。

@Test
public void testThreadPool() {
   /*ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
         new LinkedBlockingDeque<Runnable>(5));*/
   ThreadPoolExecutor newThreadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
   for (int i = 0; i < 10; i++) {
      MyThread myThread = new MyThread();
      newThreadPoolExecutor.execute(myThread);
      System.out.println(
            "线程池中线程数目:" + newThreadPoolExecutor.getPoolSize() + ",队列中等待执行的任务数目:" + newThreadPoolExecutor
                  .getQueue().size() + ",已执行玩别的任务数目:" + newThreadPoolExecutor.getCompletedTaskCount());
   }
}

9、线程池大小设置:如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1;如果是IO密集型任务,参考值可以设置为2*NCPU。

 

相关文章

    暂无相关文章
相关栏目:

用户点评