多线程
1. 进程和线程
1.1 进程
进程就是电脑中一个运行的软件,一个正在运行的程序
在做的一件事情就是一个进程
1.2 线程
线程理解为一个进程中步骤
我们在做一件事情时,分很多步骤,每一个步骤就是一个线程,同一时刻只能做一件事情
做菜是一个进程,买菜,洗菜,切菜,炒菜就是一个一个的线程。
注意:
进程间不能共享数据段地址,但同进程的线程之间可以。
- QQ软件不能使用谷歌内存中数据,360不能使用QQ内存中的数据。
- 同一个进程中的线程,可以贡献数据,会产生数据不安全的情况。
1.3 进程是如何执行的
2. 创建线程
2.1 继承Thread类
class 类 extends Thread {
public void run() {
//这个线程被cpu选中执行时,执行的业务代码
}
}
- run方法是使用当前类创建线程,被cpu选中时,实行的业务代码
package com.qfedu;
public class Demo01 {
//main方法 就是一个线程(主线程)
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
mt.start(); // 开启了一个线程,和主线程共同竞争cpu执行时间
for (int i = 0; i < 100; i++) {
System.out.println("main--"+i);
}
}
}
/*
* 创建线程
* 1. 创建类继承Thread
* 2. 重写run方法 该线程执行的业务代码
*/
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("自定义线程--"+i);
}
}
}
2.2 实现Runnable接口
实现Runnable接口,重写run方法
class 类 implements Runnable {
public void run() {
//这个线程被cpu选中执行时,执行的业务代码
}
}
Thread t = new Thread(类的对象);
package com.qfedu;
public class Demo02 {
public static void main(String[] args) {
Runnable runnable = new YourThread();
Thread t = new Thread(runnable, "线程1");
Thread t2 = new Thread(runnable, "线程2");
t.start(); // 开启了一个线程,和主线程共同竞争cpu执行时间
t2.start(); // 开启了一个线程,和线程1共同竞争cpu执行时间
}
}
/*
* 实现Runnable接口
* 重写run方法 该线程执行的业务代码
*/
class YourThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
3. 线程的状态
基本状态:
新建状态:创建线程对象
就绪状态:线程对象执行start()方法
运行状态:被cpu选中,执行run方法,如果在分配的时间片内运行结束,进入死亡状态,如果没有运行 结束,回到就绪状态。
死亡状态:run方法执行结束,进入死亡状态
package com.qfedu;
public class Demo03 {
public static void main(String[] args) {
//1. 新建状态
System.out.println("新建状态");
HerThread mt = new HerThread();
/*
* 2. 就绪状态
*
* 这个线程可以和这个进程中的其他线程,共同竞争cpu的运行时间
*
* 如果竞争到了,就执行它的run方法中代码 会进入运行状态
* - 如果在运行的时间片段内,run方法没有执行结束,那么就回到就绪状态
* - 如果run方法执行结束,该线程就over了
*/
System.out.println("就绪状态");
mt.start();
}
}
class HerThread extends Thread {
@Override
public void run() {
System.out.println("运行状态");
System.out.println("死亡状态");
}
}
4. 卖票实例
- 四个窗口各卖100张票
- 四个窗口共卖100张票
package com.qfedu;
public class Demo04 {
/*
* 四个窗口,每个窗口各卖100张票
*/
public static void main(String[] args) {
MyTicket t1 = new MyTicket("窗口1");
MyTicket t2 = new MyTicket("窗口2");
MyTicket t3 = new MyTicket("窗口3");
MyTicket t4 = new MyTicket("窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyTicket extends Thread {
private int count = 100;
public MyTicket(String name) {
super(name);
}
public void run() {
while(true) {
if(count <= 0) {
break;
}
System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
}
}
}
package com.qfedu;
public class Demo04_2 {
/*
* 四个窗口,共卖100张票
*/
public static void main(String[] args) {
Runnable runnable = new YourTicket();
Thread t1 = new Thread(runnable, "窗口1");
Thread t2 = new Thread(runnable, "窗口2");
Thread t3 = new Thread(runnable, "窗口3");
Thread t4 = new Thread(runnable, "窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class YourTicket implements Runnable {
private int count = 100;
@Override
public void run() {
while(true) {
if(count <= 0) {
break;
}
System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
}
}
}
5. 线程常用方法
//休眠,啥都不干1000毫秒,进入等待状态,1000毫秒后回到就绪状态
public static native void sleep(long millis) throws InterruptedException;
//加入,随机就变成了顺序,什么t线程执行结束,主线程才会回到就绪状态
public final void join() throws InterruptedException {}
//主动放弃cpu资源,回到就绪状态
public static native void yield();
//设置线程调用优先级 1-10 默认是5
public final void setPriority(int newPriority) {}
//是否是守护线程 非守护线程执行结束 守护线程自动结束
public final void setDaemon(boolean on) {}
- 线程在执行sleep(long millis) , join() 后进入等待状态
- 线程在执行yield()后,回到就绪状态
package com.qfedu;
public class Demo05 {
public static void main(String[] args) throws InterruptedException {
for(int i=1; i<100; i++) {
/*
* 休眠,啥都不干1000毫秒,进入等待状态,1000毫秒后回到就绪状态
*
* 在等待的过程中,是不释放锁的
*/
Thread.sleep(1000);
System.out.println("吃西瓜,现在是第" + i +"个");
}
}
}
package com.qfedu;
public class Demo06 {
/*
* 30个桃子
*
* 20个西瓜
*
* 开始随机的吃,如果桃子已经吃了10个,还有西瓜,那就先把西瓜吃完,才能吃桃子
*
*
* 随机 改成 顺序
*/
public static void main(String[] args) throws InterruptedException {
EatWatermelon t = new EatWatermelon();
t.start();
for(int i=1; i<=30; i++) {
System.out.println("吃了第"+i+"个桃子");
if(i == 10) {
//加入,随机就变成了顺序,什么t线程执行结束,主线程才会回到就绪状态
t.join();
}
}
}
}
//吃西瓜线程
class EatWatermelon extends Thread {
@Override
public void run() {
for(int i=1; i<=20; i++) {
System.out.println("吃了第"+i+"个西瓜");
}
}
}
package com.qfedu;
public class Demo07 {
public static void main(String[] args) {
YieldThread yt = new YieldThread();
yt.start();
for(int i=1; i<=100; i++) {
System.out.println("main--"+i);
if(i%3 == 0) {
Thread.yield(); // 如果是3的倍数,就主动放弃cpu资源,回到就绪状态
}
}
}
}
class YieldThread extends Thread {
@Override
public void run() {
for(int i=1; i<=100; i++) {
System.out.println("YieldThread--"+i);
if(i%8 == 0) {
yield(); // 如果是8的倍数,就主动放弃cpu资源,回到就绪状态
}
}
}
}
package com.qfedu;
public class Demo08 {
public static void main(String[] args) {
DaemonThread t1 = new DaemonThread();
t1.setName("非守护线程");
t1.setPriority(10);
DaemonThread t2 = new DaemonThread();
t2.setName("守护线程");
t2.setDaemon(true);
t2.setPriority(1);
t1.start();
t2.start();
}
}
class DaemonThread extends Thread {
@Override
public void run() {
for(int i=1; i<=100; i++) {
System.out.println(getName() + "--"+i);
}
}
}
6. 线程安全
一个进程中的多个线程在执行的过程中可能会对一个变量进行操作,这样就有可能出现数据混乱的问题,可以通过对程序加锁来解决这种情况。
synchronized (对象) { //代码块 }
- 被加锁的代码,在同一时刻只能由一个线程执行
- 想要执行加锁的代码,必须 获取 对象的锁
package com.qfedu;
public class Demo09 {
public static void main(String[] args) {
Runnable runnable = new HerTicket();
Thread t1 = new Thread(runnable, "窗口1");
Thread t2 = new Thread(runnable, "窗口2");
Thread t3 = new Thread(runnable, "窗口3");
Thread t4 = new Thread(runnable, "窗口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class HerTicket implements Runnable {
private int count = 100;
Object o = new Object();
@Override
public void run() {
while(true) {
//对代码块进行加锁,这个代码块在一个时刻,只能由一个线程执行
synchronized (o) {
if(count <= 0) {
break;
}
System.out.println(Thread.currentThread().getName()+"卖了,第"+(100 - --count)+"张票");
}
}
}
}