- 进程:计算机中特定功能的程序在数据集上的一次运行
- 线程:线程是进程的一个单元
- 多线程:一个进程中有多个线程在同时运行,如迅雷下载,迅雷软件的一次运行就是一个进程,那么在迅雷中可以同时下载多个电影,这就是多线程(每一个下载都是一个线程)
- Jvm是多线程的,在我们运行jvm的时候后台会运行垃圾回收的线程,来清理没有被引用的对象
两个小问题:
- 为什么要重写 run() 方法?
因为 run() 方法是用来封装被执行的代码 - run() 方法和 start() 方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程,然后有JVM调用此线程的 run() 方法
步骤:
- 自己定义一个类 继承 Thread
- 重写 run() 方法
- 调用 start() 方法,进入就绪状态
- 开启线程
案例代码
public class Demo1 extends Thread {
private String name;
public Demo1(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
System.out.println("下载了" + i + "%");
}
}
}
测试类
public class Demo1Test {
public static void main(String[] args) {
//1.创建我们自己的线程类
Demo1 name = new Demo1("肖申克的救赎");
//2.调用线程的 start() 方法 状态:就绪状态
name.start();
System.out.println("程序结束!!!!!");
}
}
(二)实现 Runnable 接口
步骤:
- 自己定义一个类 实现 Runnable 接口
- 重写 run() 方法
- 调用 start() 方法,进入就绪状态
- 开启线程
案例代码
public class Demo2 implements Runnable{
private String name;
public Demo2(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <=100 ; i++) {
System.out.println("下载了" + i + "%");
}
}
}
测试类
public class Demo2Test {
public static void main(String[] args) {
Thread name = new Thread(new Demo2("喜羊羊与灰太狼"));
name.start();
Thread name2 = new Thread(new Demo2("斗破苍穹"));
name2.start();
System.out.println("程序结束!!!!");
}
}
二、线程的生命周期
1.新建: 线程被new出来
2.准备就绪:线程具有执行的资格,即线程调用了start(),没有执行的权利
3.运行:具备执行的资格和具备执行的权利
4.阻塞:没有执行的资格和执行权利
5.销毁: 线程的对象变成垃圾,释放资源
当多个线程同时操作一个共享资源的时候容易造成并发问题
(一)同步代码块解决数据安全问题语法:
synchronized(锁对象){
//操作共享资源的代码
}
同步代码加在什么地方?
- 1.代码被多个线程访问
- 2.代码中有共享的数据
- 3.共享数据被多条语句操作
好处和弊端:
- 好处:解决了多线程的大户局安全问题
- 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很浪费资源的,无形中降低了程序的运行效率
实例代码
1.第一种同步处理
public class Demo1 extends Thread {
private String name;
static int tickets=100;
//定义一个共享所对象
static Object obj=new Object();
public Demo1(String name) {
this.name = name;
}
@Override
public void run() {
while (true){
//同步锁
synchronized (obj){
if (tickets>0){
System.out.println(name + "卖了的票是" + tickets-- + "号");
}else {
break;
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("票全部卖完了!!!");
}
}
测试类
public class Demo1Test {
public static void main(String[] args) {
Demo1 d1 = new Demo1("窗口一");
Demo1 d2 = new Demo1("窗口二");
Demo1 d3 = new Demo1("窗口三");
Demo1 d4 = new Demo1("窗口四");
d1.start();
d2.start();
d3.start();
d4.start();
}
}
2.第二种同步处理
public class Demo2 implements Runnable{
//定义共享数据
int tickets=100;
//定义同步对象锁
Object obj=new Object();
//重写 run() 方法
@Override
public void run() {
while (true){
//同步锁
synchronized (obj){
if (tickets>0){
System.out.println(Thread.currentThread().getName()+"卖了的票是" + tickets-- + "号");
}else {
break;
}
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("票已经全部卖完了!!!");
}
}
测试类
public class Demo2Test {
public static void main(String[] args) {
//创建一个买票对象
Demo2 demo2 = new Demo2();
Thread t1 = new Thread(demo2, "窗口一");
Thread t2 = new Thread(demo2, "窗口二");
Thread t3 = new Thread(demo2, "窗口三");
Thread t4 = new Thread(demo2, "窗口四");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
(二)同步方法解决数据安全问题
synchronized是可以加在方法上:
- 1.如果是静态方法Synchronized的所对象就是类的类对象
public class Demo4 extends Thread{
private String name;
static int tickets=100;
//定义同步对象锁
//Object obj=new Object();
public Demo4(String name) {
super(name);
this.name = name;
}
@Override
public void run() {
while (true){
if (saleTickets()){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"票已经全部卖完了!!!");
}
//如果是静态方法Synchronized的所对象就是类的类对象
public static synchronized boolean saleTickets(){
boolean is=false;
if (tickets >0){
System.out.println(Thread.currentThread().getName() + "卖了的票是" + tickets-- + "号");
}else {
is=true;
}
return is;
}
}
测试类
public class Demo4Test {
public static void main(String[] args) {
Demo4 d1 = new Demo4("一号窗口");
Demo4 d2 = new Demo4("二号窗口");
Demo4 d3 = new Demo4("三号窗口");
Demo4 d4 = new Demo4("四号窗口");
d1.start();
d2.start();
d3.start();
d4.start();
}
}
- 2.如果不是静态的方法,Synchronized如果加在对象方法上,那么他的锁是this
public class Demo3 implements Runnable{
int tickets=100;
@Override
public void run() {
while (true){
if (saleTickets()){
break;
}
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("程序结束!!!!");
}
public synchronized boolean saleTickets(){
boolean is=false;
if (tickets>0){
System.out.println(Thread.currentThread().getName() + "卖了的票是" + tickets-- + "号");
}else {
is=true;
}
return is;
}
}
测试类
public class Demo3Test {
public static void main(String[] args) {
Demo3 demo3 = new Demo3();
Thread t1 = new Thread(demo3, "一号窗口");
Thread t2 = new Thread(demo3, "二号窗口");
Thread t3 = new Thread(demo3, "三号窗口");
Thread t4 = new Thread(demo3, "四号窗口");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
四、线程间的通信
1.通信间的方法
2.生产者与消费者
生产者生成水果,如果水果没有被买走那么就不生产处于等待状态,如果水果被消费者买走就的时候消费者会通知生产者告诉他我们已经把水果买走了请生产,消费者同理,如果水果已经生产出来那么就买走,买走之后再通知生产者水果已经没了请生产。
注意:
- 1.线程间的通信共享数据一定要有同步代码块synchronized
- 2.一定要有wait和notify,而且二者一定是成对出现
- 3.生产者和消费者的线程实现一定是在while(true)里面
案例代码:
public class Basket {
//判断属性是否为空
private boolean isEmpty;
//提供 get() set() 方法
public boolean isEmpty() {
return isEmpty;
}
public void setEmpty(boolean empty) {
isEmpty = empty;
}
}
---------------------------------------------------------------------
public class Producer extends Thread{
private Basket basket;
public Producer(Basket basket) {
this.basket = basket;
}
@Override
public void run() {
while (true){
//同步锁
synchronized (basket){
try {
//判断篮子是否为空
if (!basket.isEmpty()){
//篮子不是空的,进行等待状态
basket.wait();
}
//消费端做完消费,通知我再进行生产的时候我就生产
System.out.println("生产水果");
//将篮子设置为不为空
basket.setEmpty(false);
basket.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
---------------------------------------------------------------------
public class Consumer extends Thread{
private Basket basket;
public Consumer(Basket basket) {
super();
this.basket = basket;
}
@Override
public void run() {
while (true){
//定义一个同步代码块
synchronized (basket){
try {
//判断篮子里有没有水果,如果没有我就等待通知
if (basket.isEmpty()){
//线程等待状态
basket.wait();
}
//生产者 生产去,生产完告诉我
System.out.println("消费水果");
//将篮子设置成空
basket.setEmpty(true);
//通知在这个共享对象上等待的线程
basket.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
-------------------------------------------------------------------
测试类
public class Test {
public static void main(String[] args) {
Basket basket = new Basket();
Producer producer = new Producer(basket);
Consumer consumer = new Consumer(basket);
producer.start();
consumer.start();
}
}
五、线程的优先级
(一)线程有两种调度模型:
- 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
- 抢占式调度模型:优先让优先级高的线程使用 CPU ,如果线程的优先级相同,那么会随机选一个,优先级高的线程获取的 CPU 时间片相对多一些
Java使用的是抢占式调度模型
假如计算机只有一个 CPU ,那么 CPU 在某一时刻只能执行一条指令,线程只有得到 CPU 时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有 随机性,因为谁抢到 CPU 的使用权是不一定的
(二)线程优先级的最大优先级和最小优先级
1.可以返回线程可拥有的最大优先级
static int MAX_PRIORITY
线程可以拥有的最大优先级 最大优先级为10
2.可以返回线程可拥有的最小优先级
static int MIN_PRIORITY
线程可以拥有的最小优先级 最小优先级为1
3.可以返回线程拥有的默认优先级
static int NORM_PRIORITY
分配给线程的默认优先级 默认优先级为5
(三)Thread 类中设置和获取线程优先级的方法
1.获取当前线程优先级方法
public final int getPriority()
返回此线程的优先级
2.设置方法
public final void setPriority(int newPriority)
更改此线程的优先级
- 线程优先级高仅仅表示线程获取的 CPU 时间片的几率高。但是要在次数比数多,或者多次运行的时候才能看到你想要的效果



