提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
为什么要使用多线程呢,在Java中多线程是指一个进程拥有多个线程,多线程程序并不能提高程序运行的速度,但能够提高程序运行效率让CPU的使用率更高。
注:欢迎各位指出错误
一、什么是进程什么是线程?
进程:是指一个内存中运行的程序,每个进程都有一个独立的内存空间。
线程:是进程中的一个执行路径,多个线程共享一个内存空间,线程之间可以相互切换,并发执行一个线程至少有一个线程。
二、思维导图(1)多线程概述
为方便大家理解做了一份简易思维导图
二、用法介绍
1.Runnable
代码如下(示例):
/**
* @author 26591
* 实现runnable与继承thread相比有如下优势
* 1. 通过创建任务然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况。
* 2. 可以避免单继承所带来的局限性。
* 3. 任务与线程本身是分离的,提高了程序的健壮性。
*/
public class mainRunnable {
public static void main(String[] args) {
//实现Runnable
//1. 创建一个任务对象
Myrunnable r=new Myrunnable();
//2. 创建一个线程,并为其分配一个任务
Thread t=new Thread(r,"锄禾日当午");
//thread.currentThread表示当前正在执行线程
System.out.println(Thread.currentThread().getName());
//3. 执行这个线程
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("汗滴禾下土"+i);
}
}
}
结果
2.线程中断与守护
代码如下(示例):
/**
* @author 26591
*/
public class ThreadInterrupt {
public static void main(String[] args) {
//线程的中断
//一个线程是一个独立的执行路径,它是否应该结束,应该由其自身的决定
//线程分为守护线程和用户线程
//守护线程:守护用户线程,当最后一个用户线程结束时,所有守护线程自动死亡。
Thread t1= new Thread(new MyRunnable());
//守护线程设置方式,main线程死亡t1线程自动结束
t1.setDaemon(true);
t1.start();
for (int i = 0; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//给线程t1打上中断标记
t1.interrupt();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// e.printStackTrace();
//程序员可以控制线程是否继续运行
System.out.println("检查到标记我自杀了");
return;
}
}
}
}
}
本应该继续执行的代码当主程序执行完成后线程检测到中断标记自我中断。
并且此处也表明了t1线程属于守护线程就算是没有中断标记,当用户线程也就是主线程结束后该线程也会自动消亡。
3、线程安全
方式一同步代码块(隐式锁)
/**
* @author 26591
* 线程同步:synchronized
*/
public class ThreadSafetyDemo1 {
public static void main(String[] args) {
//线程不安全
//解决方案1.同步代码块
//格式:synchronized(锁对象){}排队执行
//任何对象都可以成为锁对象
Runnable run=new Ticket();
//多个线程同时争抢同一资源时容易出现问题
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
private Object o=new Object();
@Override
public void run() {
//注:使用同一把锁要避免锁对象不为一
//错误示例Object o=new Object();
while (true) {
//锁对象
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
}else {
break;
}
}
}
}
}
}
方式二同步方法(隐式锁)
/**
* //使用synchronized (){}到方法
* @author 26591
*/
public class ThreadSafetyDemo2 {
public static void main(String[] args) {
//线程不安全
//解决方案2 同步方法
Runnable run=new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
@Override
public void run() {
while (true) {
//锁对象
boolean flag= sale();
if (!flag){//!flag=false
System.out.println("票已售空");
break;
}
}
}
public synchronized boolean sale(){
/*synchronized对象是调用者this
若被静态修饰则为字节码同步对象例如
Ticket.class
*/
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
return true;
}else {
return false;
}
}
}
}
方案三显示锁Lock
public class ThreadSafetyDemo3 {
public static void main(String[] args) {
//线程不安全
// 解决方案3。显示锁 Lock 子类 Reentrantlock
//
Runnable run=new Ticket();
//多个线程同时争抢同一资源时容易出现问题
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
//票数
private int count = 10;
//显示锁:参数为True表示 是公平锁谁先来谁先得
private Lock l=new ReentrantLock();
@Override
public void run() {
while (true) {
//锁住
l.lock();
//锁对象
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票:" + count);
}else {
break;
}
l.unlock();
}
}
}
}
思维导图(2)常用方法概述
思维导图(3)线程池
1.如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
2.线程池的好处
降低资源消耗。
提高响应速度。
提高线程的可管理性
(1)缓存线程池
public class CacheThreadPool {
/**
* 缓存线程池。
* (长度无限制)
* 任务加入后执行的流程
* 1.判断线程池是否存在空闲线程
* 2.存在则使用
* 3.不存在,则创建线程 并放入线程池,然后使用
* @param args
*/
public static void main(String[] args) {
ExecutorService service= Executors.newCachedThreadPool();
//指挥线程池中执行新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
//当最开始的三个线程执行完毕后此时线程池中拥有三个空闲线程选取其中一个执行下次任务
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
执行结果:
(2) 定长线程池
public class FixedLengthThreadPool {
/**
* 定长线程池
* (长度时指定的数值)
* 任务加入后的执行流程
* 1.判断线程池是否存在空闲线程
* 2.存在即使用
* 3.不存在空闲线程,且线程池未免的情况下,则创建线程 并放入线程池,然后使用
* 4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
* @param args
*/
public static void main(String[] args) {
ExecutorService service= Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
执行结果:
(3) 单线线程池
public class SingleThreadPool {
/**
* 单线程线程池
* 执行流程:
* 1.判断线程池 的那个线程 是否空闲
* 2.空闲则使用
* 3.不空闲,则等待 池中的单个线程空闲后 使用
*/
public static void main(String[] args) {
ExecutorService service= Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
执行结果:
(4) 缓存线程池
public class PeriodicFixedLengthTP {
/**
* 周期任务 定长线程池
* 执行流程:
* 1.判断线程池是否存在空闲线程
* 2.存在即使用
* 3.不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池,然后使用
* 4.不存在空闲线程,且线程池已满的情况下,则等在线程池存在空闲线程
*
* 周期性任务执行时:
* 定时执行,当某个时机触发时,自动执行某任务。
* @param args
*/
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 定时执行一次
* 参数1.定时执行的任务
* 参数2.时常数字
* 参数3.时常数字的时间单位TimeUnit的常量指定
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("锄禾日当午");
}
},5, TimeUnit.SECONDS);
*/
/**
* 周期性执行任务
* 参数1.任务
* 参数2.延迟时长数字(第一次执行在什么时间以后)
* 参数3.周期性时长数字
* 参数4.时常数字的单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("汗滴禾下土");
}
},3,1,TimeUnit.SECONDS);
}
}
执行结果:
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了JAVA多线程的使用,而JAVA多线程使我们快速便捷地处理数据的效率。