一、进程和线程的区别
进程
1、就是独立的程序,通过系统分配,获取系统的CPU、内存、显卡、网络和磁盘这些资源。
2、由多个线程组成,而且至少有一个线程
3、进程申请的是系统的资源
4、具有独立性和互斥性
线程
1、线程就是进程中的一个功能
2、抢占式运行:CPU执行程序是按照时间片来进行执行的。
3、资源共享:一个当前程序中的线程可以共享程序的资源,如CPU
4、线程申请的是进程的资源
二、线程的优缺点
优点:
1、提高资源的利用率
2、电脑可以同时执行多个程序,提高工作效率
3、提升用户体验
劣势:
1、加重CPU的负担
2、降低CPU执行其他线程的概率,用户体验会卡顿
3、共享资源的问题
4、死锁(避免)
三、创建自定义线程的两种方式
Thread类是线程的基类
方式1:自定义一个线程类,继承extends Thread,重写run方法
方式2:自定义线程类,实现一个接口 Runnable ,并实现run方法
推荐第二种方式
原因:java中继承是单继承,一个类只能继承一个类,如果一个类继承了Thread类就无法继承其他类,当时可以实现多个接口
示例
class MyThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("继承Thread的自定义线程");
}
}
}
class MyThread2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("实现Runnable的自定义线程");
}
}
}
public class Demo1 {
public static void main(String[] args) {
/**
* 如何来执行线程中的代码
* 方法1:
* 创建自定义线程的类对象
* 使用start方法开启线程
* 【错误操作】:
* 不能直接调用run
*/
//此线程开始执行; Java虚拟机调用此线程的run方法。
MyThread1 myThread1 = new MyThread1();
myThread1.start();
/**
* 方法2:
* 创建自定义线程类的对象
* 把自定义线程对象当成一个参数放到Thread类中
* 使用Thread类对象调用start()
*/
MyThread2 myThread2 = new MyThread2();
Thread thread = new Thread(myThread2);
thread.start();
for (int i = 0; i < 100; i++) {
System.out.println("这个是main主线程的线程代码");
}
}
}
四、线程中常用的方法
Thread类的构造方法:
Thread():创建一个Thread线程的类对象,没有指定目标代码和线程名字
Thread(Runnable target):创建一个Thread类的对象,传入的参数是一个Runnable接口实现的类对象。
Thread(Runnable target,String name):创建一个Thread类的对象,传入的参数是一个Runnable接口实现的类对象并给线程起名字
Thread类的静态方法:
static Thread currentThread():该方法在哪里执行就获取的是哪里的线程对象
static void sleep():让当前线程休眠指定的毫秒数
Thread类的成员方法:
void setName(String name):设置线程的名字
String getName():获取线程的名字
void setPriority(int new Priority):设置线程的优先级,优先级只能增加执行概率,不能确定一定先执行。线程默认的为5, 1~10 最高的是10 最低的1
int getPriority(): 获取当前线程优先级
示例:
public class Demo2 {
public static void main(String[] args) {
Thread t1 = new Thread();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("吃早饭");
}
}
},"线程2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("吃午饭");
}
}
},"线程3");
System.out.println(t1);//Thread[Thread-0,5,main] 线程的名字 优先级 在哪个线程组里
System.out.println(t2);
System.out.println(t3);
System.out.println(t3.getName());//线程3
System.out.println(Thread.currentThread());//Thread[main,5,main]
t2.setPriority(10);
System.out.println(t2.getPriority());//10
t1.start();
t2.start();
t3.start();
}
}
五、同步代码块
用来处理共享资源问题
例如:
1、ATM机,一个人在办理业务的时候,里面要锁门之后其他人要排队。
2、上厕所大号,你要锁上门别人来排队,你结束以后,开锁之后,别人上。
示例
电影院 《你好,李焕英》 100张票
这里有三种订票方式,需要共同卖这100张票,100张票是三个销售渠道的共享资源
每一个销售渠道都是一个线程。
淘票票 猫眼 美团
问题一:
这个100张票该怎么保存???选择什么类型的数据???
int类型的数据
问题二:
创建三个销售线程类的对象,如果ticket是成员变量,每一个销售线程里面都会有自己的成员变量。这样ticket就不是共享资源,相互不干扰的。-----不行
定义在方法中,只能定义在run方法中,运行一个run,ticket会重新定义一次----不行
类变量:静态的成员变量保存在数据区,可以提供给所有当前类使用。而且一处修改,处处修改,是一个 不错的选择
问题三:存在一张票卖出去3次的情况
解决方案:同步代码块
sysnchronized(/锁对象/) {
//加锁的代码
}
1.在这个代码块内,只能出现一个线程执行
2.锁对象可以是任意类型的对象
3.同步代码块包含的代码越少越好
4.Thread.sleep();不会开启锁对象
class SaleTicket implements Runnable {
private static int ticket = 100;
@Override
public void run() {
synchronized ("锁") {
while (true) {
if (ticket > 0) {
/*
* 为什么这个地方只能捕获???
* 因为这个地方实现的是接口中的方法,使用到了注解@Override
* 要求开启了重写的严格检查。重写要求方法的声明必须保持一致
*异常的抛出属于方法声明的一部分。
*而在Runnable接口中,run方法没有抛出异常。
*所以只能捕捉
*/
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售出了第" + (101 - ticket) + "张票");
ticket--;
} else {
System.out.println("卖完了");
break;
}
}
}
}
}
public class Demo3 {
public static void main(String[] args) {
Thread t1 = new Thread(new SaleTicket(),"淘票票");
Thread t2 = new Thread(new SaleTicket(),"美团");
Thread t3 = new Thread(new SaleTicket(),"猫眼");
t1.start();
t2.start();
t3.start();
}
}