创建线程4种方法
- ----继承thread 重写run方法
----实现runnable接口 重写run方法 -作为参数传入thread
- —实现callable接口 重写call方法—构造Funturetask并传入实现call接口的对象(实现run方法)-----传入thread
区别runnable好处:1.可以返回参数 2.可以抛异常 3.泛型化(类似模板)
-----使用线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(new fool()); //实现runnable的类
executorService.submit(new example());//实现callable的类
executorService.shutdown();
ThreadPoolExecutor service = (ThreadPoolExecutor) executorService; //体现线程管理前,需要进行强转 体现多态性
service.setCorePoolSize();
service.setMaximumPoolSize();
service.setKeepAliveTime();
executorService.shutdown();
好处:
- 响应速度更快
- 降低资源消耗
- 便于线程管理
ps:创建接口殊途同归,都是为了重写thread 的run方法
解决线程安全问题:同步机制
核心
- 共享数据
- 同步监视器(锁)
Synchronized
- 同步方法 默认为当前对象(方法所在的对象)的指针 确定锁更快捷,但是也需要结合具体问题分析
- 同步代码块 手动输入锁的对象 静态方法中 当前类 .class 非静态方法 当前对象 this 但是也要分析是否一直为同一对象
- (确保每个同步方法或块中用的是同一把锁,不然无法解决线程安全问题(反向理解相当于可以有多个进入方法的钥匙) )
多个线程分别对相同数据在不同方法中操作时,应当把方法放在相同数据所在类内,这样就有了天然的锁
线程通信方法:
wait notify notifyall 都只能在同步代码块或方法中使用,但是所有类型的对象都有这个方法
因此lock实现同步机制解决线程安全问题时,无法使用 上述三个方法
ReentrantLock
在继承thread的进程中,
可能需要将reentrantlock 属性设置为静态
实现runnable,callable接口时,天然为同一把锁
在try{sth.lock }finally{ sth.unlock } 中使用,确保锁一定被解锁 上锁的位置是在操作共享数据的代码块之前,解锁的位置在其之后
使用的优先顺序: lock>同步代码块>同步方法 (按照锁住的范围来排名,范围越大,可提前加载的资源越少,效率越低,排名越后)