Thread线程
线程创建方式
- 继承
Thread
类 - 实现
Runnable
接口 - 实现
Callable
接口,Callable
接口可以通过实现call()
方法实现方法运行,并通过FutureTask
来获取返回结果
// 通过继承Thread类实现Run方法,并调用start方法运行。通过start方法运行可以交给CPU进行资源分配。
class A extends Thread{
@Override
public void run() {
System.out.println("ok");
}
}
public class test {
public static void main(String[] args) {
A a = new A();
a.start();
}
}
// 通过实现Runnable接口,重写run方法,然后实例化对象借助Thread实力进行运行。
class A implements Runnable{
@Override
public void run() {
System.out.println("ok");
}
}
public class test {
public static void main(String[] args) {
new Thread(new A()).start();
}
}
class A implements Callable<String>{
@Override
public String call() throws Exception {
return "ok";
}
}
public class test {
public static void main(String[] args) {
FutureTask<String> futureTask = new FutureTask<>(new A());
new Thread(futureTask).start();
}
}
线程池相关操作
- Future和FutureTask
- 线程池的创建
- 线程拒绝策略
Future 和 FutureTask
- Future常常作为线程池中submit方法的返回值的接收,可以通过调用get()方法获取线程返回值;
- FutureTask类实现了Runnable和Future接口,其构造函数可以接收Runnable和Callable实现类,然后可以通过线程池的submit或者execute方法进行运行,也可以直接通过Thread运行,最后可以调用get方法获取返回值;
class A implements Callable<String> {
@Override
public String call() throws Exception {
return "ok";
}
}
class B implements Runnable{
@Override
public void run() {
System.out.println("ok");
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new A());
new Thread(futureTask).start();
FutureTask<String> futureTask1 = new FutureTask<>(new B(),"backinfo");
new Thread(futureTask1).start();
System.out.println(futureTask1.get());
System.out.println(futureTask.get());
}
}
ExecutorService service = Executors.newSingleThreadExecutor();
Future<String> future = service.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "say helloWorld!!!";
}
});
System.out.println(future.get());// 通过get返回结果
线程池的创建
newCachedThreadPool
:重用以前线程,无可用则创建,空闲则移除;newFixedThreadPool(int size)
:创建指定大小的线程池。通过队列管理newScheduledThreadPool(int size)
:可以设置运行几次以及时间间隔newSingleThreadExecutor
:创建只有一个线程的线程池,这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去- 线程池的运行方法有两种:
- submit:返回一个future类型,可以获取返回值
- execute:直接运行,无返回值
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i + " ");
Future f = pool.submit(c);
list.add(f);
}
for (int i = 0; i < taskSize; i++) {
Runnable c = new Myrunnable(i + " ");
Future f = pool.submit(c,:"backInfo");
list.add(f);
}
pool.shutdown();
for (Future f : list) {
System.out.println("res:" + f.get().toString());
}
线程拒绝策略
线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。
AbortPolicy
:直接抛出异常,阻止系统正常运行。CallerRunsPolicy
:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。DiscardOldestPolicy
:丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。DiscardPolicy
:该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这是最好的一种方案。- 以上内置拒绝策略均实现了RejectedExecutionHandler接口,若以上策略仍无法满足实际需要,完全可以自己扩展RejectedExecutionHandler接口。
线程的相关操作
Thread.sleep()
使得当前线程休眠一定的时间,放弃CPU使用权,但是不会放弃资源锁,是Thread
的静态函数,不论谁调用sleep
方法,休眠的总是当前线程。getPriority()
和setPriority(int newPriority)
,获取线程的优先级join()
,通过线程实例对象调用join方法,使得当前线程等待join线程结束Thread.yield()
方法使得当前线程让出CPU资源;interrupt()
通过调用此方法发出一个信号,通常用于在线程阻塞时通知退出阻塞;Object.wait()
作为Object
对象的方法,用于休眠当前线程,通过线程的对象调用,并放弃当前持有的锁,必须在synchronized
中使用。Object.notify[all]()
作为Object
对象的方法,用于唤醒等待此对象的线程,必须在synchronized
中使用。
Synchronized关键字作用
- 修饰一个代码块,被修饰的代码块称为同步语句块,作用的对象是调用这个代码块的对象。
- 修饰一个方法,被修饰的方法称为同步方法,作用的对象是调用这个方法的对象。
- 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
- 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。
- 总结:被
synchronized
修饰的对象,其所有synchronized方法被锁住,非synchronized方法正常使用。
// synchronized修饰代码块,只锁定指定的对象的synchronized修饰的代码。this指当前对象,当有线程进入this对应的代码块,则此对象的所有synchronized修饰的代码全部锁住,非synchronized修饰的代码块任然可以进入。
public class A implements Runnable {
public static void main(String[] args) throws InterruptedException {
}
Object lock1 = new Object();
Object lock2 = new Object();
@Override
public void run() {
// 第一把锁
synchronized (lock1) {
System.out.println("我是lock1,我叫"+ Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"lock1运行结束");
}
// 第二把锁
synchronized (lock2) {
System.out.println("我是lock2,我叫"+ Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"lock2运行结束");
}
synchronized (this) {
System.out.println("我是this,我叫"+ Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"this运行结束");
}
}
}
// 当synchronized修饰方法的时候,如果有县城进行这个某个实例对象的方法中,那么这个实例对象其他被synchronized修饰的方法同时也会被锁住。
class A{
public synchronized void say(B b){
System.out.println("sayA");
b.get();
}
public synchronized void get(){
System.out.println("getA");
}
}
class B{
public synchronized void say(A a){
System.out.println("sayB");
a.get();
}
public synchronized void get(){
System.out.println("getB");
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
A a = new A();
B b = new B();
new Thread(()->a.say(b)).start();
b.say(a);
}
ThreadLocal详解
总的来说ThreadLocal只是一个工具类,而ThreadLocalMap是一个类似于Map的键值对:ThreadLocalMap<ThreadLocal,value>
,为什么说是类似Map的,是因为ThreadLocalMap不是一个Map,核心是一个Entry数组,Entry对象有一个属性:Value,如何获取这个对象是通过ThreadLocal计算一个hash值作为索引获取。其中ThreadLocalMap是当前线程Thread的一个属性,所以要想保存数据,可以创建一个ThreadLocal对象,然后通过set方法可以在当前线程中保存这个Thread Local和一个值,在这个set方法中会获取当前的线程,并保存数据,而这个ThreadLocal是一个弱引用。
由于一个java项目中经常需要多线程操作且线程的创建与销毁都会产生CPU开销,所以在代码开发过程中,常用线程池进行多线程的操作。因此存在大量的线程复用的情况,一个线程的生命周期较长。在Thread线程的类中
class Thread{
ThreadLocal.ThreadLocalMap map = null;
}
class ThreadLocal{
// 静态内部类
static class ThreadLocalMap{
// 静态内部类采用弱引用
static class Entry extends WeakReference<ThreadLocal<?>>{
}
}
}
class Demo{
// threadlocal使用时首先初始化一个ThreadLocal对象,然后通过set方法进行数据存储,从而达到线程之间隔离的效果
// 此时,在这个线程中就会存在一个ThreadLocalMap<ThreadLocal,Integer>,其中key为创建的threadlocal对象,value为设置的值,
// 使用完后,如果没有remove,就会导致内存泄漏的问题,
ThreadLocal<Integer> threadlocal = ThreadLocal.withinit(()=>{ return 0;});
threadlocal.set(1);
threadlocal.get();
threadlocal.remove();
}