多线程
线程与进程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的空间
线程:1.是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
2.线程实际上是在进程基础上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程
分时调度:所有线程轮流使用cpu的使用权,平均分配每个线程占用cpu的时间
抢占式调度:1.优先让优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选择一个(线程随机性),java使用的为抢占式调度
2.cpu使用抢占式调度模式在多个线程间进行高速的切换,对于cpu的一个核断而言,某个时刻,只能执行一个线程,而cpu的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行,其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让cpu的使用率更高
同步与异步&并发与并行
同步:排队执行,效率低但是安全
异步:同时执行,效率高但是数据不安全
并发:指两个或多个事件在同一个时间段内发生
并行:指两个或多个事件在同一时刻发生(同时发生)
继承Thread
每个线程都拥有自己的栈空间,公用一份堆内存
由一个线程所执行的方法,那么这个方法在这个线程里面
public class Demo1 {
/*
多线程技术
*/
public static void main(String[] args) {
//Thread
//开启了两个线程,谁快谁慢是不确定的,谁先抢到谁先执行
MyThread m = new MyThread();
m.start();
for (int i=0;i<10;i++){
System.out.println("汗滴禾下土"+i);
}
}
}
public class MyThread extends Thread{
/**
* run方法就是线程要执行的任务方法
*/
@Override
public void run() {
//这里的代码 就是一条新的执行路径
//这个执行路径的触发方法,不是调用run方法,而是通过thread对象的start()来启动任务f
for (int i=0;i<10;i++){
System.out.println("锄禾日当午"+i);
}
}
}
实现Runnable
public class Demo2 {
/**
* 多线程技术
* 实现Runnable和继承Thread相比有如下优势
* 1.通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行和同任务的情况
* 2.可以避免单继承带来的局限性
* 3.任务与线程本身是分离的,提高了程序的健壮性
* 4.后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
*
* @param args
*/
public static void main(String[] args) {
//实现Runnable
//1.创建一个任务对象
MyRunnable r = new MyRunnable();
//2.创建一个线程并为其分配一个任务
Thread t = new Thread(r);
//3.执行这个线程
t.start();
for (int i=0;i<10;i++){
System.out.println("疑是地上霜"+i);
}
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
// 线程的任务
for (int i=0;i<10;i++){
System.out.println("床前明月光"+i);
}
}
}
public class Demo3 {
public static void main(String[] args) {
//匿名内部类
new Thread(){
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("hhhhhhhhh"+i);
}
}
}.start();
for (int i=0;i<10;i++){
System.out.println("疑是地上霜"+i);
}
}
}
Thread类
getname 返回线程名称
getid 返回线程标识符
getpriority 返回线程优先级
setpriority 设置线程优先级
通过变量做标记的方式结束线程
sleep 暂时停止执行当前正在执行的线程,休眠
setDaemon 表示是否是守护线程或用户线程
main方法主线程,其余方法子线程
守护线程:当所有用户线程死亡之后自动死亡
用户线程:
设置和获取线程名称
public class Demo4 {
public static void main(String[] args) {
//如何获取线程的名称
System.out.println(Thread.currentThread().getName());
// 不设置名称也会有默认的名称
new Thread(new MyRunnable(),"锄禾日当午").start();
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
new Thread(new MyRunnable()).start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
}
线程休眠sleep
public class Demo5 {
public static void main(String[] args) throws InterruptedException {
//线程呢个的休眠
for (int i=0;i<10;i++){
System.out.println(i);
Thread.sleep(1000);
}
}
}
线程阻塞
线程阻塞(耗时操作)
所有比较消耗时间的操作,比如说常见的文件读取
线程的中断
public class Demo7 {
public static void main(String[] args) {
//线程的中断
//一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定
Thread t1 = new Thread(new MyRunnable());
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();
System.out.println("发现");
return; //线程自杀
//由外部干涉一个完整的流程死亡是不合理的
}
}
// 给线程t1添加中断标记
}
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();
}
}
}
}
}
守护线程
线程:分为守护线程和用户线程
用户线程:当一个进程不包含任何的存活的用户线程时,进行结束
守护线程: 守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。
//设置为守护线程
t1.setDaemon(true);
线程安全问题
public class Demo9 {
public static void main(String[] args) {
//线程不安全
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(count>0){
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("出票成功。余票"+count);
}
}
}
}
线程安全1-同步代码块
线程不安全的原因:多个线程同时执行,去争抢一个数据,去同时操作一个数据,那么导致某个数据在判断和自己使用时不一样,导致运行不符合预期。
public class Demo10 {
/**
*
* @param args
*/
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;
}
}
}
}
}
}
线程安全2-同步方法
public class Demo11 {
/**
*
* @param args
*/
public static void main(String[] args) {
//线程不安全
//解决方法:同步方法
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() {
while (true) {
boolean flag = sale();
if (!flag){
break;
}
}
}
public synchronized boolean sale(){
//在方法内部,谁调用锁对象就是谁==this
//如果该方法是静态的话就是class,比如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;
}
return false;
}
}
}
线程安全2-显式锁Lock
同步代码块和同步方法都属于隐式锁
显式锁和隐式锁的区别
public class Demo12 {
/**
* @param args
*/
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;
//显式锁l
private Lock l = new ReentrantLock();
private Object o = new Object();
@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();
}
}
}
}
公平锁和非公平锁
公平锁能保证:老的线程排队使用锁,新线程仍然排队使用锁
非公平锁保证:老的线程排队使用锁;但是无法保证新线程抢占已经在排队的线程的锁。
//显式锁 l: fair参数为true就表示是公平锁
线程死锁
public class Demo13 {
public static void main(String[] args) {
//线程死锁
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p).start();
c.say(p);
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
public MyThread(Culprit c,Police p){
this.c = c;
this.p = p;
}
@Override
public void run() {
p.say(c);
}
}
//罪犯
static class Culprit{
public synchronized void say(Police p){
System.out.println("罪犯:你放了我,我放人质");
p.fun();
}
public synchronized void fun(){
System.out.println("罪犯被释放了,罪犯也放了人质");
}
}
//警察
static class Police{
public synchronized void say(Culprit c){
System.out.println("警察:你放了人质,我放过你");
c.fun();
}
public synchronized void fun(){
System.out.println("警察救了人质,但是罪犯跑了");
}
}
}
多线程通信问题
wait:导致当前线程等待他被唤醒,通常是通知或中断
notify:唤醒正在此对象监控器上等待的单个线程
notifyAll:唤醒等待此对象监视器的所有线程
public class Demo14 {
/**
* 多线程通信问题,生产者和消费者问题
* @param args
*/
/**
* 多线程通信问题, 生产者与消费者问题
* @param args
*/
public static void main(String[] args) {
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
//厨师
static class Cook extends Thread{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
f.setNameAndSaste("老干妈小米粥","香辣味");
}else{
f.setNameAndSaste("煎饼果子","甜辣味");
}
}
}
}
//服务生
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i=0;i<100;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//食物
static class Food{
private String name;
private String taste;
//true 表示可以生产
private boolean flag = true;
public synchronized void setNameAndSaste(String name,String taste){
if(flag) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void get(){
if(!flag) {
System.out.println("服务员端走的菜的名称是:" + name + ",味道:" + taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
线程的六种状态
带返回值的线程Callable
之前的进程,和主进程一起走
callable既可以实现一起走,也可以等callable完成给出结果主进程再进行
get 如果需要,最多等待计算完成的给定时间,然后检索其结果(如果可用)
public class Demo15{
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable c = new MyCallable();
FutureTask task = new FutureTask<>©;
// task.isDone();//判断子线程是否执行完毕
// task.cancel();//取消执行
new Thread(task).start();
Integer j = task.get();
System.out.println(“返回值”+j);
for (int i=0;i<10;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
// Thread.sleep(3000);
for (int i=0;i<10;i++){
try {
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(i);
}
return 100;
}
}
}
线程池概述
缓存线程池
public class Demo16 {
//线程池
/**
* 缓存线程池
* 长度无限制
* 任务加入后的执行流程
* 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(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
定长线程池
public class Demo17 {
/**
* 定长线程池
* (长度是指定的数值)
* 任务加入后的执行流程
* 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() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
单线程线程池
public class Demo18 {
/**
* 单线程线程池
* 执行流程:
* 1.判断线程池 的哪个线程 是否空闲
* 2.空闲则使用
* 3.不空闲则等待池中的单个线程空闲后使用
* @param args
*/
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()+"锄禾日当午");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"锄禾日当午");
}
});
}
}
周期任务定长线程池
public class Demo19 {
/**
* 周期任务 定长线程池时机触发时,自动执行某任务
* 周期任务执行时
* 定时执行,当某个
* @param args
*/
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 1.定时执行一次
* 参数1.定时执行的任务
* 参数2.时长数字
* 参数3.时长数字的时间单位Time
*/
/*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("汗滴禾下土");
}
},5,1,TimeUnit.SECONDS);
}
}
Lambda表达式
public class Demo21 {
public static void main(String[] args) {
//这个接口必须只有一个抽象方法才可以使用lambda表达式
// print(new MyMath() {
// @Override
// public int sum(int x, int y) {
// return x+y;
// }
// },100,200);
//替换成lambda表达式
print((int x, int y) -> {
return x+y;
},100,200);
}
public static void print(MyMath m,int x,int y){
int num = m.sum(x,y);
System.out.println(num);
}
static interface MyMath{
int sum(int x,int y);
}
}