Java多线程
Posted big-data-sky
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程相关的知识,希望对你有一定的参考价值。
什么是程序:有序严谨的指令集称为程序。
什么是进程:程序的同时多运行称为进程。
什么是线程:程序中不同的执行路经称为线程,线程是程序的最小执行单位。
多线程:如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称为”多线程”
- 多线程的好处:
-
- 充分利用cpu的资源 编程简单
- 简化编程模型 效率高
- 带来良好的用户体验 易于资源共享
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
在Java中创建线程的几种方式
-
继承Thread类创建线程
//继承Thread类创建线程 public class MyThread extends Thread public void run() for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()); public static void main(String[] args) MyThread mt = new MyThread(); //需使用start()方法启动线程,如果直接调用线程中run()方法会: //1.只有主线程一条执行路径2.依次调用了两次run()方法 mt.start();
-
实现Runnable接口创建线程
//实现Runnable接口创建线程 public class MyRunnable implements Runnable @Override public void run() for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()); public static void main(String[] args) //创建线程对象 MyRunnable mr = new MyRunnable(); Thread t = new Thread(mr); t.start();
-
实现Callable接口创建Thread线程
public class MyThread implements Callable<Integer> @Override public Integer call() throws Exception System.out.println("子线程:"+Thread.currentThread().getName()); return null; public class Test public static void main(String[] args) FutureTask<Integer> ft = new FutureTask<Integer>(new MyThread()); Thread t = new Thread(ft,"01"); System.out.println("主线程:"+Thread.currentThread().getName()); t.start();
-
通过Future 创建线程
public class CallableThreadTest implements Callable<Integer> public static void main(String[] args) CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<>(ctt); for(int i = 0;i < 100;i++) System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i); if(i==20) new Thread(ft,"有返回值的线程").start(); try System.out.println("子线程的返回值:"+ft.get()); catch (InterruptedException e) e.printStackTrace(); catch (ExecutionException e) e.printStackTrace(); @Override public Integer call() throws Exception int i = 0; for(;i<100;i++) System.out.println(Thread.currentThread().getName()+" "+i); return i;
线程调度方法:
- setPriority(int newPriority);更改线程的优先级
线程优先级由1~10表示,1最低,默认优先级为5
优先级高的线程获得CPU资源的概率较大
- MAX_PRIORITY:静态常量 表示最大优先级
- MIN_PRIORITY:静态常量 表示最小优先级
- sleep(long millis);在指定毫秒数内让当前正在执行的线程休眠
让线程暂时睡眠指定时长,线程进入阻塞状态
睡眠时间过后线程会再进入运行状态
millis为休眠时长,已毫秒为单位
调用sleep()方法需处理InterruptedException异常
- join(); 等待该线程终止
Join(long millis);
Join(long millis,int nanos);
使当前线程暂停执行,等待其他线程结束后在继续执行本线程
Millis:已毫秒为单位 的等待时长
Nanos:要等待的附加纳秒时长
需处理InterruptedException异常
- yield(); 暂停当前正在执行的线程对象,并执行其他线程
暂停当前线程,允许其他具有相同优先级的线程获运行机会
该线程处于就绪状态,不转为阻塞状态
只是提供一种可能,但是不能保证一定会实现礼让
- interrupt(); 中断线程
- isAlive();测试线程是否处于活动状态
线程的同步:
线程不同步会遇到的问题:(当多个线程共享同一资源时,一个线程未完成全部操作的时候,其他线程会修改数据,造成数据不安全问题。
线程同步)
- 使用synchronized修饰的方法控制对类成员变量的访问
- 访问修饰符 synchronized 返回类型 方法名(参数列表)……
synchronized 访问修饰符 返回类型 方法名(参数列表)……
Synchronized()内为需同步的对象,通常为this
效果与同步方法相同
synchronized就是为当前的线程声明一把锁
多个并发线程访问同一资源的同步代码块时
同一时刻只能有一个线程进入synchronized(this)同步代码块
当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
package demo01; //子线程在调用son方法的时候一上来就能获取同步监视器this //父线程在调用father函数的时候一上来就是获取不到 //字父线程执行的四种情况: 子 父 父 子 子子 父父 //wait:Object (导致线程阻塞,并释放对同步监视器的占用 ) //notify:Object (唤醒线程的作用,提示后续等待该锁的线程,可以进行访问) public class FatherAndSon boolean flag=true; public void son() synchronized (this) //子一定是先执行的 if(!flag) try this.wait(); catch (InterruptedException e) e.printStackTrace(); for (int i = 0; i < 3; i++) System.out.println("儿子线程执行第"+(i+1)+"次"); flag=false;//1:把机会让给父线程 2、防止子线程再次执行 this.notify(); public void father() synchronized (this) //父是后执行的 if(flag) try this.wait(); catch (InterruptedException e) e.printStackTrace(); for (int i = 0; i < 5; i++) System.out.println("老子线程执行第"+(i+1)+"次"); flag=true;//1:把机会让给子线程 2、防止父线程再次执行 this.notify();
package demo01; //在Java中什么是所谓的同一资源 static //只存在一个对象的情况下 ticket //多个对象: a.ticket b.ticket c.ticket //保证一种情况: //synchronized:方法(整个所修饰的方法体都是线程同步的范围)(this:)====>synchronized(this) //synchronized:代码块(代码块中的内容是线程同步的范围) public class Test public static void main(String[] args) throws InterruptedException final FatherAndSon fs=new FatherAndSon(); Thread t1=new Thread(new Runnable() @Override public void run() //子线程的线程体中 for (int i = 0; i < 10; i++) fs.son(); ); t1.start(); //父线程的线程体中 for (int i = 0; i < 10; i++) fs.father();
线程池:
尽量保证任务数不要超过最大线程数+阻塞队列的长度
- 使用线程池的好处:
- 重用存在的线程,减少对对象创建,消亡的开销
- 有效控制最大并发数,提高系统资源使用率
- 定时执行,定期执行
- 线程池所在的包:
Java.util.concurrent
顶级接口Executor,真正的线程池接口时ExecutorService
Java.util.concurrent.Executors类提供创建线程池的方法
自定义线程池:
/** * * 创建自定义线程池 * * */ public class Test public static void main(String[] args) //创建自定义线程池 尽量保证:任务数不要超过最大线程数+阻塞队列的长度 ThreadPoolExecutor executor = new ThreadPoolExecutor(5,7,300,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(4)); for(int i=0;i<12;i++) executor.execute(new MyRunnable(i)); System.out.println("线程池中线程数:"+executor.getPoolSize()+",对列中等待任务执行数:"+executor.getQueue().size()+",已经执行完的任务数:"+executor.getCompletedTaskCount()); executor.shutdown(); class MyRunnable implements Runnable int num;//第几个任务 public MyRunnable(int num) super(); this.num = num; @Override public void run() System.out.println("正在执行任务"+num); try Thread.sleep(2000); catch (InterruptedException e) e.printStackTrace(); System.out.println("任务"+num+"执行完毕");
后台线程的特点:
- 随着前台线程的运行而运行
- 随着前台线程的消亡而消亡
- SetDaemon():将线程设置为后台线程 GC 传入为true是将线程设置为后台线程,默认false
- isDaemon():判断是否是后台线程
以上是关于Java多线程的主要内容,如果未能解决你的问题,请参考以下文章