目录
1.守护线程
简单来说,就是没有任何一个线程的时候,JVM就需要退出了,这个时候守护线程也会退出,主要完成垃圾回收等功能>
在没有其他线程存在时,守护线程自动退出,坚持到最后一个结束
- 设置守护线程
//Thread.setDameon()
//true启动守护线程,false关闭守护线程
t1.setDaemon(true);
2.定时器 Timer
计划任务,只要有一个任务监听
就会是一个线程
1 执行任务的类 , 2 执行任务起始时间 3 执行任务间隔时间
public static void main(String[] args) throws ParseException {
//创建计时器对象
Timer t= new Timer();
//创建线程实现类
//一个实现类对象就是一个线程
processor_02 p1 = new processor_02();
processor_02 p2 = new processor_02();
processor_02 p3 = new processor_02();
//设置时间格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM HH:mm:ss SSS");
//将实现类当做一个线程每隔一段时间进行输出
t.schedule(p1, sdf.parse("2020-3 00:00:00 000"), 1000);
t.schedule(p2, sdf.parse("2020-3 00:00:00 000"), 1000);
t.schedule(p3, sdf.parse("2020-3 00:00:00 000"), 1000);
}
}
class processor_02 extends TimerTask{
@Override
public void run() {
//创建时间对象
Date d = new Date();
//时间格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM HH:mm:ss SSS");
//打印时间格式
System.out.println(sdf.format(d));
}
3.锁对象
由于对象中有一个方法加锁,那么,在这个线程访问还没结束时,其他加加锁方法不得被访问
//某对象,其中有加锁方法
class Student{
public synchronized void m1(){
System.out.println("m1已运行!!!");
//某个线程在这个方法中时,其他的线程不能访问其他加锁方法
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void m2(){
System.out.println("m2已运行!!!");
}
//不加锁的方法可以访问
public void m3(){
System.out.println("m3已运行!!!");
}
}
4.死锁
两个线程各访问一个对象后,没有及时解锁,然后又互相需要各自的对象时,都不让步造成的
- 代码块锁
- synchronized(xxx){} 代码块锁,可以锁类,也可以锁对象
- 如果锁对象,当访问该代码块锁的时候,该对象中所有的代码块锁和加锁的成员方法都被锁定
- 同理 访问对象中加锁的成员方法的时候,代码块锁也会被锁定
- 如果是锁类,当访问该代码块的时候,该类中所有的代码块锁和加锁的静态方法都被锁定
- 同理 访问类中加锁的静态方法的时候,代码块锁也会被锁定
public void run() {
//访问o1对象,并加锁
synchronized (o1){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//访问o2对象,并加锁
synchronized (o2){
System.out.println("t1被执行了");
}
}
}
5.挂起/唤醒
当一个加锁的方法被访问时,此类中其他的加锁方法也不能被访问,除非被挂起wait(),然后被其他在这个对象上运行的线程唤醒this.notifyAll();后才可以执行
public synchronized void printodd(){
while (count % 2 == 1) {
System.out.println(Thread.currentThread().getName()+" :"+count);
count++;
this.notifyAll();
try {
Thread.sleep(500);
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public synchronized void printeven(){
while (count % 2 == 0){
System.out.println(Thread.currentThread().getName()+" :"+count);
count++;
this.notifyAll();
try {
Thread.sleep(500);
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
6.线程池
线程池的作用 :
- 线程池作用就是限制系统中执行线程的数量
- 根据系统的环境情况,可以自动或者手动来设置线程数量,以达到运行的最佳效果
- 少了浪费系统资源,多了造成系统拥挤效率不高
- 用线程池控制线程数量,其他线程排队等候
- 一个任务 执行完成,再从队列中取最前面的任务开始执行
- 如果队列中没有等待进程,线程池的这个资源处于等待状态
- 当一个新任务需要运行时,如果此时线程池中还有等待中的工作线程时,可以直接开始运行
- 否则需要进入等待队列
- 为什么要使用线程池
- 1 减少了创建 和销毁线程的次数,因为每个工作线程都可以被重复使用,可执行多个任务
- 2 可以根据系统的承受能力,调整线程池中的线程数量,防止因为消耗过多的内存,导致服务器死机
- (每个线程需要大概1MB内存,线程开的越多,消耗内存越大,最后导致死机)
- 使用线程池代码
public static void main(String[] args) {
//创建线程池,可缓存,可以自动回收空闲线程,池子规模不限制,数量不固定
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
cachedThreadPool.execute(new Processor_07());
}
System.out.println(Thread.currentThread().getName()+"++++++++++++++++");
//关闭线程池
cachedThreadPool.shutdown();
}
创建一个固定 长度线程池,可控制线程最大并发数
超出此数量的线程,会在队列中等待
可以规定线程数量,这些线程在池子中循环使用
- 使用固定线程池代码
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
fixedThreadPool.execute(new Processor_08());
}
System.out.println(Thread.currentThread().getName()+"++++++++++++++++");
//关闭线程池
fixedThreadPool.shutdown();
}
- 周期\定时\常量池
创建一个固定长度线程池,支持定时及周期性执行任务
public static void main(String[] args) {
//创建定时周期执行任务常量池
ScheduledExecutorService scheduledThreadPool = Executors
.newScheduledThreadPool(5);
//延迟5秒执行
// scheduledThreadPool.schedule(new processor_08_(), 5,TimeUnit.SECONDS);
//延迟5秒执行,并间隔一秒
scheduledThreadPool.scheduleAtFixedRate(new processor_09_(), 5, 1, TimeUnit.SECONDS);
}
单线程线程池,只创建一个线程,如果这个 线程因为异常结束,那么会有一个新的线程来替代他
- 该线程保证所有的任务的执行顺序,按照任务的提交顺序执行,谁先来谁先执行
- 适用于一个一个任务执行的情况
- 单线程常量池代码
ExecutorService singleThreadExecutor = Executors
.newSingleThreadExecutor();
// 一次性创建10个请求
for (int i = 0; i < 10; i++) {
// 只能依次执行
singleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()
+ " : " + 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
7.懒汉优化(多线程)
- 懒汉式单例模式优化
//直接锁住方法效率会降低,因为有些已创建对象的线程原本不需要等待即可得到返回值
// public synchronized static singleLethon creat(){
// if (sl == null) {
// sl = new singleLethon();
// }
// return sl;
//
// }
//再次优化
public static singleLethon creat(){
//此判断是:判断是否为已创建对象,否的话直接返回
if (sl == null) {
synchronized(singleLethon.class){
//此判断是:判断已经进入外循环等待的线程应该是都没有创建对象
//但是一旦把锁放开就会给等待的线程都创建新的对象
//所以二次判断:判断如果已经有对象就不再创建
if (sl == null) {
sl = new singleLethon();
}
}
}
return sl;
}