多线程——Thread、Runnable

5 篇文章 0 订阅
1 篇文章 0 订阅

多线程——Thread、Runnable

进程 :一个系统正在运行的应用程序,是资源分配的最小单位,一个进程可以拥有一个至多个线程(进程结束时,会销毁该进程下的所有线程)。
线程:进程的一个执行单元同时也是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();
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值