JAVA-----线程学习2(Runnable接口)

实现Runnable接口

源代码

@FunctionalInterface   注解声明是函数式接口
public interface Runnable {
    public abstract void run();
}

问题:定义run()方法没有返回值

public class Test2 {
	public static void main(String[] args) {
		Thread t1 = new Thread(new MyRunnable());
		t1.start();
		// 方法2.2
		new Thread(new Runnable() {
			public void run() {
				for (int i = 0; i < 10; i++) {
					System.out.println("中间手...."+Thread.currentThread());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		// 方法2.3
		new Thread(() -> {
			for (int i = 0; i < 10; i++) {
				System.out.println("右手...."+Thread.currentThread());
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
}

//方法2.1
class MyRunnable implements Runnable {
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("左手...."+Thread.currentThread());
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

使用Callable和Future接口创建线程

具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装
Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程

java.util.concurrent.Callable  有时java.util.concurrent包可以称为juc包

@FunctionalInterface  注解用于声明当前接口是一个函数式接口

@FunctionalInterface  注解用于声明当前接口是一个函数式接口
public interface Callable<V> {  这里<>中用于声明返回值的类型
   V call() throws Exception;  真正执行的方法,有返回值,允许抛出异常
}

线程的构造器为

public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }

Future接口

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);  可以取消正在运行的线程
    boolean isCancelled(); 判断是否取消
    boolean isDone(); 判断线程是否在运行中
    V get() throws InterruptedException, ExecutionException; 获取线程执行的结果
    V get(long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;

Java针对Future接口提供的具体实现类FutureTask

源代码:

public class FutureTask<V> implements RunnableFuture<V>
	
public interface RunnableFuture<V> extends Runnable, Future<V>

构造器

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
	public void run() {
            Callable<V> c = callable;
            ...
                    result = c.call();
    }
public class Test3 {
	public static void main(String[] args) throws Exception {
		// 为了获取返回值,所以需要使用FutureTask中提供的get方法
		FutureTask<Integer>[] arr = new FutureTask[10];
		for (int i = 1; i <= 10; i++) {
			int begin = (i - 1) * 100 + 1;
			int end = i * 100;
			// 定义Callable的实现
			Callable<Integer> c = new Callable<Integer>() {
				public Integer call() throws Exception {
					int res = 0;
					for (int k = begin; k <= end; k++)
						res += k;
					return res;
				}
			};
			// 构建FutureTask对象,其中包含Callable对象
			FutureTask<Integer> ft = new FutureTask<>(c);
			// 不是必须的,只是证明FutureTask实现了Runnable接口
			Runnable r = ft;
			arr[i - 1] = ft;
			// 启动线程,构建Thread对象时,要求参数类型为Runnable接口类型
			// 线程执行时会自动调用run方法,而FutureTask中提供的run方法会调用Callable对象的call方法
			new Thread(r).start();
		}
		int res = 0;
		for (FutureTask<Integer> tmp : arr)
			res += tmp.get(); // main线程执行到这里时,会自动阻塞等待子线程tmp执行结束,执行结束后获取call方法的返回值
		System.out.println(res);
	}
}

注意:FutureTask实现了Future和Runnable接口,所以new Thread(futureTask),
当执行thread.start()方法时会自动调用Callable接口实现中的call方法。当调用futureTask.get()方法时可以获取对应的线程对象的执行结果,如果线程并没有返 回时,当前线程阻塞等待

使用线程池创建线程

享元模式Flyweight Pattern主要用于减少创建对象的数量,以减少内存占用和提高性能
。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象
结构的方式

  • 优点:大大减少对象的创建,降低系统内存的使用,以提高程序的执行效率。
  • 缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,
而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若

干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。

new Thread的弊端

  1. 每次new Thread新建对象性能差。
  2. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争及可能占用过多系统资源导致死机或oom。
  3. 缺乏更多功能,如定时执行、定期执行、线程中断。

OutOfMemoryError即OOM的可能原因?

  • 数据库的cursor没有及时关闭
  • 未关闭InputStream outputStream
  • Bitmap 使用后未调用recycle()
  • static等关键字
  • 非静态内部类持有外部类的引用context泄露
  • 流量/数据量峰值:应用程序在设计之初均有用户量和数据量的限制,某一时刻,当用户数量或数据量突然达到一个峰值,并且这个峰值已经超过了设计之初预期的阈值,那么以前正常的功能将会停止,并触发java.lang.OutOfMemoryError: Java heap space异常。
  • 内存泄漏:特定的编程错误会导致你的应用程序不停的消耗更多的内存,每次使用有内存泄漏风险的功能就会留下一些不能被回收的对象到堆空间中,随着时间的推移,泄漏的对象会消耗所有的堆空间,最终触发java.lang.OutOfMemoryError: Java heap space错误。

线程池的好处

  • 重用存在的线程,减少对象创建、消亡的开销,性能佳
  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞
  • 提供定时执行、定期执行、单线程、并发数控制等功能。

线程池的工作原理

  1. 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。
  2. 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步
  3. 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,  
long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue)

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
                              

  •     corePoolSize线程池的核心大小
  •     maximumPoolSize线程池中允许的最大线程数
  •     workQueue阻塞任务队列
  •     threadFactory线程工厂
  •     RejectedExecutionHandler饱和处理策略

守护线程

正常情况下,非守护线程的执行时间和主线程无关,即使主线程已经结束,也不会影响子线程的运行。
 守护线程是为其它线程提供服务的线程

  •  守护进程的特点是当程序中主线程结束时,守护线程会自动中止

如何将一个子线程设置为守护线程

  •  在一个线程调用start启动之前,调用方法thread.setDaemon(true);就可以将thread线程设置为守护线程.

守护线程一般应该是一个独立的线程,它的run()方法是一个无限循环

  • 守护线程与其它线程的区别是,如果守护线程是唯一运行着的线程,程序会自动退出

线程组

Thread类的构造器

public Thread(String name) {
        this(null, null, name, 0);
    }
    
public Thread(Runnable target, String name) {
        this(null, target, name, 0);
    }

Thread线程类中toString方法的定义

public String toString() {
	ThreadGroup group = getThreadGroup();//获取当前线程对象所属的线程组
    if (group != null) {
    	return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]";
	} else {
    	return "Thread[" + getName() + "," + getPriority() + "," + "" + "]";
    }
}

所有线程都隶属于一个线程组。那可以是一个默认线程组【main】,亦可是一个创建线程时明确指定的组

说明

  • 在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组
  • 若创建多个线程而不指定一个组,它们就会与创建它的线程属于同一个组

创建线程指定对应的线程组

public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target, String name)

 *ThreadGroup myThreadGroup = new ThreadGroup("My Group");//创建线程组
 *Thread myThread = new Thread(myThreadGroup,"a thread");  //定义线程时指定对应的线程组
 *theGroup = myThread.getThreadGroup();//获取线程对象所属的线程组

主要通途:可以通过线程组,对线程组中一组线程进行统一管理,而不是一个一个的管理。注意显示线程时,如果线程显示为null表示线程已经消 亡(执行结束)。在具体开发中很少使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值