目录
目录
一、多线程
1.多线程的原理
同一时间内,CPU只能处理1条线程,只有1条线程在工作(执行);多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。
程序启动运行main时候,java虚拟机启动一个进程,其中有一个主线程(main方法调用时候被创建)。随着调用Thread对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。
2.线程类Thread的介绍
构造方法:
public Thread() :分配一个新的线程对象。
public Thread(String name) :分配一个指定名字的新的线程对象。
public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
public Thread(Runnable target,String name) :分配一个指定目标新的线程对象并指定名字。
常用方法:
public String getName() :获取当前线程名称。
public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run() :此线程要执行的任务在此处定义代码。
public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread() :返回对当前正在执行的线程对象的引用。
3.创建线程的两种方式
方式一:
描述:
将一个类声明为一个Thread的子类。
这个子类应该重写run类的方法,然后可以分配并启动子类的实例
步骤:
a.创建子类 继承 Threand (子类也是一个代表线程的类)
b.子类中重写run方法(编写任务代码的地方)
c.创建子类对象(创建一个线程对象)
d.启动线程,调用线程对象.start();
注意:
启动线程必须调用start,而不是调用run
方式二:
描述:
实现Runnable接口方式
步骤:
a.定义Runnable接口的实现类,并重写该接口的run()方法
b.用public Thread(Runnable target)的构造方法创建Thread对象
c.启动线程,调用线程对象.start();
4.两种创建线程方式的区别
实现Runnable接口比继承Thread类所具有的优势:
(1)可以避免java中的单继承的局限性。
(2)实现解耦操作,任务可以被多个线程共享,任务和线程独立。
(3)线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
5.使用匿名内部类创建线程
使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
二、线程安全
1.线程安全问题出现的原因
当有多个线程在同时运行,这些线程同时运行一段代码(同一段任务代码,同一个run方法),操作同一个共享数据时,这时候可能就会出现线程的安全问题,即线程不安全的。
2.线程同步
使线程按照一定的先后次序进行运行
(1)同步代码块
synchronized(同步锁){
需要同步操作的代码
}
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
①锁对象 可以是任意类型。
②多个线程对象 要使用同一把锁。
(2)同步方法
public synchronized void method(){
可能会产生线程安全问题的代码
}
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
(3)Lock锁
java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
public void lock() :加同步锁。
public void unlock() :释放同步锁。
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
三、线程的状态
1.线程的六种状态
(1)新建状态(new)
线程刚被创建,但是并未启动。还没调用start方法。
(2)可运行状态(Runnable)
线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
(3)受阻塞状态(Blocked)
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
(4)限时等待状态(TimeWaiting)
同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep()、锁对象.wait()。
(5)无限等待状态(Waiting)
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
(6)消亡状态(Teminated)
因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
四、代码练习
1.卖包子案例
包子铺卖包子,有包子时则不生产,买家在没包子时不消费包子,有包子时消费包子。
public class BaoZi {
boolean flag=false;//判断包子有没有
}
public class BaoZiTest {
public static void main(String[] args) {
BaoZi bz = new BaoZi();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
//如果包子没有了,调用wait()方法进入waiting状态
synchronized (bz) {
if (bz.flag == false) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("吃货醒来了");
System.out.println("吃货吃包子");
System.out.println("吃货吃完了...");
//包子吃完了,使flag=false并唤醒做包子线程
bz.flag = false;
bz.notify();
}
}
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (bz) {
//如果还有包子,调用wait()方法进入waiting状态
if (bz.flag == true) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("包子铺发现没包子了");
System.out.println("包子铺做包子");
System.out.println("包子已经上架了...");
//包子做出来了,使flag=true并唤醒吃包子线程
bz.flag = true;
bz.notify();
}
}
}
});
t2.start();
System.out.println("hello");
}
}
2.过山洞案例
请按要求编写多线程应用程序,模拟多个人通过一个山洞: 1.这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒; 2.随机生成10个人,同时准备过此山洞,并且定义一个变量用于记录通过隧道的人数。 显示每次通过山洞人的姓名,和通过顺序;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
public class GuoShanDong {
ArrayList<String> names = new ArrayList<>();
int a=1;
public GuoShanDong() {
Collections.addAll(names, "路飞", "索隆", "娜美", "乔巴", "香吉士",
"乌索普", "弗兰奇", "甚平", "布鲁克", "罗宾");
}
public synchronized void guoShanDong() {
Random r = new Random();
if (names.size() > 0) {
int i = r.nextInt(names.size());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println((a++)+"---"+Thread.currentThread().getName() + "---" + names.remove(i) + "正在通过山洞...");
}
}
}
public class Test09 {
static int a = 1;
public static void main(String[] args) {
GuoShanDong gsd = new GuoShanDong();
int i = 0;
new Thread(new Runnable() {
@Override
public void run() {
while (gsd.a <= 10) {
gsd.guoShanDong();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (gsd.a <= 10) {
gsd.guoShanDong();
}
}
}).start();
}
}