多线程——Thread、Runnable
进程 :一个系统正在运行的应用程序,是资源分配的最小单位,一个进程可以拥有一个至多个线程(进程结束时,会销毁该进程下的所有线程)。
线程:进程的一个执行单元同时也是cpu调度的最小单位,一个进程下的所有线程共享该进程的所有资源(一个线程结束时,不会影响在该进程下的其它线程,死锁除外)。
进程在被启动时就被分配了虚拟的地址空间,他的线程会在这个已经分配好的内存上再申请内存资源,线程的执行需要这个内存资源,这个资源会不定时的随机分配给其中一个线程(因此在同一时间下,只会有一个线程运行,我们感觉不出来,是因为资源在线程之间切换的频率太快)
Thread类:
Thread的构造方法:
1、将类声明为Thread的子类,需要重写父类的run()方法:
wait:
线程:进程的一个执行单元同时也是cpu调度的最小单位,一个进程下的所有线程共享该进程的所有资源(一个线程结束时,不会影响在该进程下的其它线程,死锁除外)。
线程的生命周期:
新建、就绪状态、运行状态、阻塞状态、死亡
进程在被启动时就被分配了虚拟的地址空间,他的线程会在这个已经分配好的内存上再申请内存资源,线程的执行需要这个内存资源,这个资源会不定时的随机分配给其中一个线程(因此在同一时间下,只会有一个线程运行,我们感觉不出来,是因为资源在线程之间切换的频率太快)
Thread类:
Thread的构造方法:
Thread优先级字段:
Thread的常用方法:
以及Object的方法(等待和唤醒):
创建线程的两种方法:
1、将类声明为Thread的子类,需要重写父类的run()方法:
//继承了Thread的类
public class ThreadDemo extends Thread {
// 重写了父类的run方法
@Override
public void run() {
// getName()方法是父类的方法,用于获取线程的名字
for (int i = 0; i < 100; i++) {
System.out.println(getName() + "正在运行第 " + i + " 次for循环");
}
}
}
public static void main(String[] args) {
// Thread
// 创建线程对象(ThreadDemo继承了Thread)
ThreadDemo td1 = new ThreadDemo();
ThreadDemo td2 = new ThreadDemo();
// 修改线程的名字,方便区分,此处调用的是父类(Thread)的方法
td1.setName("Thread --1");
td2.setName("Thread --2");
// 启动线程,使用start()方法,系统会自动调用ThreadDemo对象重写的run()方法
td1.start();
td2.start();
}
2、声明实现了Runnable接口,并实现run()方法:
// 实现了Runnable接口的类
public class RunnableDemo implements Runnable {
// 实现了Runnable接口的run()方法(Runnable接口中只有一个run方法)
@Override
public void run() {
// Thread.currentThread()会获取当前正在执行的线程的对象,使用这个对象.getName()获取线程的名字
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "正在运行第 " + i
+ " 次for循环");
}
}
}
public static void main(String[] args) {
// Runnable
/***
* 创建线程对象,将RunnableDemo对象做参数传入Thread(Runnable只有run方法,其他方法需要依赖Thread)
* 参数2:线程名字(可在调用构造方法时,同时设置线程名)
* 使用Runnable接口的原因:
* 1、使用Runnable只创建了一个对象,更好的实现了数据和操作的分离
* 2、因为一个类只能继承一个父类,当一个类已经继承了其它父类,若要使用多线程,可以通过实现Runnable完成(避免了继承的局限性)。
*/
RunnableDemo rd = new RunnableDemo();
Thread rd1 = new Thread(rd, "Runnable --1");
Thread rd2 = new Thread(rd, "Runnable --2");
Thread rd3 = new Thread(rd, "Runnable --3");
Thread rd4 = new Thread(rd, "Runnable --4");
// 启动线程,此处使用的是Thread的方法
rd1.start();
rd2.start();
rd3.start();
rd4.start();
}
线程的优先级:
// 创建线程对象
RunnableDemo rd = new RunnableDemo();
Thread rd1 = new Thread(rd, "Runnable --1");
Thread rd2 = new Thread(rd, "Runnable --2");
Thread rd3 = new Thread(rd, "Runnable --3");
Thread rd4 = new Thread(rd, "Runnable --4");
/***
* 设置线程优先级
* MAX_PRIORITY == 10 MIN_PRIORITY == 1 NORM_PRIORITY == 5
* 优先级的范围是从1到10(默认为5)优先级最高并不代表他会被最先执行,但是优先级高的得到的时间片多
*/
rd1.setPriority(1);
rd2.setPriority(4);
rd3.setPriority(6);
rd4.setPriority(10);
synchronized 同步锁(同步代码块、同步方法):
使用同步锁将需要同步的代码包起来,最先执行到同步锁位置的线程,会获取到锁(锁相当于通行证),然后执行同步锁包起来的代码,在此期间其它线程由于没有获取到锁,会被同步锁阻挡在外,只有等到持有锁的线程执行完同步锁中的所有代码时,才会释放锁,让其他线程去争抢锁。【多个线程执行同一段代码或方法时,会出现不安全的因素,将代码加上同步锁,可以始终保持只有一个线程在调用这个方法或代码,确保了安全性(可以将同步锁中的代码。视为一个整体)】
同步代码块的使用:
格式(同步代码块的 '锁' 可以是任意类型):
synchronized(锁){
...需要同步的代码...
}
如:
// 实现了Runnable接口的类
public class RunnableDemo implements Runnable {
// 初始化循环次数为200(在此初始化是为了保证多线程共用这一个变量)
private int count = 200;
/***
* 创建了一个Object对象(锁),用作synchronized的参数;
* 需要保证多个线程使用的锁是一样的
* 一个锁只能被一个线程所持用,当持有锁的线程执行完synchronized中的所有代码时,也会释放锁的拥有权
*/
private Object object = new Object();
// 实现了Runnable接口的run()方法(Runnable接口中只有一个run方法)
@Override
public void run() {
while (true) {
/***
* 此处使用java关键字synchronized,创建同步代码块使得只有持有锁的线程才可进入,
* 当持有锁的线程执行完了synchronized中的所有代码时,才会释放锁
* 使用锁的原因:
* 由于变量count是由多个线程共同使用,若不加锁会造成数据混乱
*/
synchronized (object) {
// 判断条件是否成立
if (count > 0) {
// Thread.currentThread()会获取当前正在执行的线程的对象,使用这个对象.getName()获取线程的名字
System.out.println(Thread.currentThread().getName()
+ " 线程执行了一次循环,还剩 " + count-- + " 次循环需要执行");
} else {
// 当条件不成立时,跳出while循环
break;
}
}
}
}
}
【若此处不加锁】
/***
* 若此处不加同步代码块:
* 假设‘线程1’取得count变量等于100,但在执行‘count--’之前,
* 执行资源被分配到了‘线程2’,那‘线程2’也会取得count变量等于100,
* 导致最后‘线程1’和‘线程2’都打印出了>>>>"线程执行了一次循环,还剩100 次循环需要执行"的结果
*/
while (true) {
// 判断条件是否成立
if (count > 0) {
// Thread.currentThread()会获取当前正在执行的线程的对象,使用这个对象.getName()获取线程的名字
System.out.println(Thread.currentThread().getName()
+ " 线程执行了一次循环,还剩 " + count-- + " 次循环需要执行");
} else {
// 当条件不成立时,跳出while循环
break;
}
}
同步方法的使用:
格式:public void synchronized(){
...需要同步的代码...
}
如:
/***
* 同步方法:在方法的返回值类型前面的任意地方,加上synchronized关键字即可
* 没有获取到锁的线程会被阻挡在方法之外
* 非静态同步方法的锁是:this对象
* 静态同步方法的锁是:类的字节码文件对象————类.class
*/
public synchronized void synMethod() {
// 需要同步的内容
System.out.println(Thread.currentThread().getName() + "正在执行同步方法");
}
【由于使用了同步机制,java也会花费相应的资源去管理这个机制,所以同步的内容越少越好,因此优先使用同步代码块而不是同步方法】
线程的等待和唤醒机制(wait、notify):
wait和notify方法都属于Object类的方法,每个对象都可以使用,但前提是必须在同步锁中使用。
wait:
notify:
wait和notify的使用:
//共享数据类
public class WaitAndNotifyByDogFood {
// 狗粮的数量
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
//线程1 dog
public class WaitAndNotifyByDog extends Thread {
// 初始化参数
private WaitAndNotifyByDogFood dogFood;
private int count;
// 通过构造器赋值
public WaitAndNotifyByDog(WaitAndNotifyByDogFood dogFood) {
this.dogFood = dogFood;
}
@Override
public void run() {
while (true) {
// 同步代码块
synchronized (dogFood) {
if ((count = dogFood.getNumber()) == 0) {
System.out.println("狗粮都吃完了,狗狗饿了");
try {
// 让线程等待(同时该线程会释放锁),当线程被唤醒时,会从这里继续向下执行
dogFood.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
while (count > 0) {
System.out.println(getName() + " 吃了一袋 狗粮,还剩下 "
+ --count + "袋");
}
// 修改两个线程的共享数据
dogFood.setNumber(count);
// 唤醒线程(只会唤醒等待队列里的第一个)
dogFood.notify();
}
}
}
}
}
//线程2 master
public class WaitAndNotifyByMaster extends Thread {
// 初始化参数
private WaitAndNotifyByDogFood dogFood;
private int count;
// 通过构造器赋值
public WaitAndNotifyByMaster(WaitAndNotifyByDogFood dogFood) {
this.dogFood = dogFood;
}
@Override
public void run() {
while (true) {
// 同步代码块
synchronized (dogFood) {
if ((count = dogFood.getNumber()) != 0) {
System.out.println("狗粮还能吃一阵子");
try {
// 让线程等待(同时该线程会释放锁),当线程被唤醒时,会从这里继续向下执行
dogFood.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
while (count < 10) {
System.out.println(getName() + " 在商店买了一袋狗粮,现在共有 "
+ ++count + " 袋");
}
// 修改两个线程的共享数据
dogFood.setNumber(count);
// 唤醒线程(只会唤醒等待队列里的第一个)
dogFood.notify();
}
}
}
}
}
public class WaitAndNotifyByMain {
public static void main(String[] args) {
// 创建一个实体类对象,供多线程使用,同时也用作同步代码块的锁(确保两个线程使用相同的锁)
WaitAndNotifyByDogFood entity = new WaitAndNotifyByDogFood();
// 创建线程对象1,并将锁对象传入
WaitAndNotifyByDog dog = new WaitAndNotifyByDog(entity);
dog.setName("小白");
// 创建线程对象2,并将锁对象传入
WaitAndNotifyByMaster master = new WaitAndNotifyByMaster(entity);
master.setName("主人");
// 启动线程
dog.start();
master.start();
}
}