8.JAVA多线程概括
一、多线程的定义
进程
在Java虚拟内存中,一个java应用程序相当与一个进程,每个进程都有一个独立的内存空间,进程与进程之间若要进行数据交互等通信是十分困难的。
线程
但是作为进程的一部分,线程作为进程的最小执行单位。
在一个线程中,每个线程提供了可以在同一时间内可以执行不同的功能。可以充分利用并分配CPU,从而达到效率的最大化。
但是由于线程之间是可以共享内存的,所以线程之间的数据交互是十分复杂的,避免不了数据冲突的问题,当在不需要多线程操作程序中尽量使用Java的单一顺序执行代码。
二、一个线程的生命周期
线程是一个多态执行的过程,包括从产生到结束。
生命周期包括五个状态
新建状态
使用new或者继承Thread的子类创建一个线程对象后的状态,直到使用start()方法
就绪状态
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态
如果就绪状态的线程获取 CPU 资源,就可以执行 run()方法,此时线程便处于运行状态。
处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
-
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
-
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
-
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。
-
当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
死亡状态
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
三、创建线程对象
Java 提供创建线程的方法:
-
通过继承 Thread 类
-
通过实现 Runnable 接口
Thread构造方法
public Thread()
无参构造创建一个普通的线程,将自动为线程命名
public Thread(Runnable target)
参数类型为Runnable实现类的实例,将在此实现类中的run方法运行该新建的线程
public Thread(Runnable target,String ThreadName)
参数类型为Runnable实现类的实例,字符串为定义线程的名字
通过继承Thread的子类来创建线程对象
public class ThreadDemo extends Thread {
//通过重写Thread父类的run方法来定义该线程所执行的任务
@Override
public void run() {
/*功能方法*/
}
}
通过实现 Runnable 接口
public class RunnableDemo implement Runnable{
@Override
public void run() {
/*功能方法*/
}
}
Thread类常用方法
public void start() 使该线程开始执行;java虚拟机调用该线程的 run 方法。
public void run()
此方法体为线程所执行的操作
public void interrupt()
中断某个线程的休眠状态,如sleep(),join()等,其线程唤醒后将会抛出中断异常
public boolean isInterrupted()
判断线程是否已经中断
final boolean isAlive()
判断线程是否处于活动状态
final String getName()
获取线程自定义名称
final int getPriority()
获取当前线程的优先级
public static Thread currentThread()
获取当前线程对象并返回
final String setName(String name)
设置当前线程名称
final int setPriority(int newPriority)
设置当前线程的优先级,优先级分为三个类型
分别是Thread的常量:MAX_PRIORITY,MIN_PRIORITY,NORM_PRIORITY
final void join() throws InterruptedException
使当前线程独占CPU资源,直到这个线程结束
public static void sleep(long millis) throws InterruptException
使当前线程休眠给定的毫秒时长,此时线程被中断,到达给定的毫秒时长后此线程将被唤醒
public static void yield()
暂停当前线程,并将占用的资源让出并执行其他线程
四、线程通信
Java中可以创建多个线程对象,当一个线程访问一个属性时,要避免其他的线程对此属性进行访问,否则多个线程同时访问一个属性时,会导致数据混乱。对一个线程进行数据访问时,实现线程安全。
synchronized关键字
Java提供此关键字用于修饰需要进行线程同步的对象,当一个线程对象访问属性时进行锁定,在此线程结束之后释放同步锁,其他线程才能获取对其数据进行操作的权限。
同步代码块
//通过Runnable接口的实现类创建线程对象
public class SynCodeBlock implements Runnable{
/*同步代码块的使用*/
@Override
public void run() {
Thread current = Thread.currentThread();//获取当前被启动的线程
//输入需要使用代码同步锁的线程实例
synchronized (this){
for (int i = 0; i <= 3 ; i++) {
System.out.println(current.getName()+"正在占用资源,请稍等···");
try {
Thread.sleep(1000);//延时输出
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//TestSynCodeBlock
/*测试同步代码块*/
SynCodeBlock synCodeBlock = new SynCodeBlock();//构建带有run方法的线程实例
//定义三个线程在同一run方法中执行
Thread one = new Thread(synCodeBlock,"线程一");
Thread two = new Thread(synCodeBlock,"线程二");
Thread three = new Thread(synCodeBlock,"线程三");
//设置三个线程的优先级
one.setPriority(Thread.MIN_PRIORITY);
two.setPriority(Thread.MAX_PRIORITY);
three.setPriority(Thread.NORM_PRIORITY);
one.start();
two.start();
three.start();
同步对象锁
public class SynObject implements Runnable{
/*同步对象锁*/
private Account account;
public SynObject(){
if (account==null) {
account = new Account();//创建需要锁定的对象
}
Thread one = new Thread(this,"张三");
Thread two = new Thread(this,"李四");
Thread three = new Thread(this,"王五");
three.setPriority(Thread.MAX_PRIORITY);
one.start();
two.start();
three.start();
}
@Override
public void run() {
synchronized (account){
Thread current = Thread.currentThread();
for (int i=1;i<=4;i++){
int num = 1000;
account.setBalance(num);
System.out.println(current.getName()+"存入"+account.getBalance());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
同步方法锁
/*同步方法锁的用法*/
private int balance = 500;
@Override
public void run() {
//调用互斥方法,锁定只能同时一个线程调用的方法
doBill();
}
//使用synchronized修饰的方法称为互斥方法
private synchronized void doBill() {
Thread c = Thread.currentThread();
for (int i = 0; i <= 5; i++) {
if (c.getName().equals("发工资")){
balance+=Math.random()*100;
System.out.println(c.getName()+"余额:"+balance);
}
if (c.getName().equals("日常消费")){
balance-=Math.random()*100;
System.out.println(c.getName()+"余额:"+balance);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("余额还剩:"+balance);
}
/*测试同步方法锁*/
SynMethod synMethod = new SynMethod();
Thread income = new Thread(synMethod,"发工资");
Thread expend = new Thread(synMethod,"日常消费");
income.setPriority(Thread.MAX_PRIORITY);
income.start();
expend.start();