什么是进程
在了解线程之前先了解下什么是进程。
认识进程先从程序开始,程序是对数据描述与操作的代码的集合。进程是程序的一次动态执行过程。一个应用程序运行的时候就会产生一个进程
进程有啥特点
1.进程是系统运行的基本单位
2.每个进程都有自己独立的一块内存空间、一组系统资源
3.每个进程的内部数据和状态都是完全独立的
什么是线程
1.线程是进程中执行运算的最小单位,可以完成一个独立的顺序控制流程
2.是CPU调度和分配的基本单位
进行和线程是个什么关系
一个进程可以有多个线程,如果将CPU比喻成工厂,那么进程就是一个一个的车间,而线程就是车间里的工人。
什么是多线程
1.一个进程中同时运行了多个线程,用来完成不同的工作。
2.多个线程会交替占用CPU资源,这个过程是随机分配的,而非真正的并行执行
多线程有什么好处呢
1.可以充分的利用CPU资源,提高代码的性能
2.带来良好的用户体验
3.更好的软件设计和架构
什么是主线程
1.每个程序至少自动拥有一个线程,称为主线程,当程序价加载到内存时启动主线程。
2.main()方法是主线程的入口
3.产生其他子线程的线程
4.必须最后完成执行,因为它执行各种关闭动作
Thread类
1.位于java.lang包中
2.支持多线程编程
常用方法:
1.currentThread()
静态方法,获取当前主线程
2.getName()
获取线程名称
3.setName()
设置线程名称
如何编写一个线程
用户编写的线程一般是指除了主线程之外的其他线程
创建一个线程通常有两种方法
1.继承java.lang.Thread类
2.实现java.lang.Runnable接口
1.继承java.lang.Thread类
实现步骤:
1.定义一个类继承Thread类,并重写run()方法,在run()方法中实现执行操作
2.创建线程对象
3.调用start()方法启动线程
代码实现:
//通过继承Thread类来创建线程
public class MyThread extends Thread{
//重新run()方法
public void run(){
System.out.println("线程名称:"+Thread.currentThread().getName());
System.out.println("我是通过继承Thread来的线程");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();//创建线程对象
myThread.start();//启动线程
}
}
运行结果:
是否可以直接调用run()方法来启动线程呢
直接调用会被当做main()方法中的普通方法来执行,失去了线程的意义
2.实现java.lang.Runnable接口
实现步骤:
1.定义一个类实现java.lang.Runnable接口,并实现run()方法,在run()方法中实现执行操作
2.创建线程对象
3.调用start()方法启动线程
Thread类的构造方法
1.public Thread(Runnable target)
以Runnable接口对象作为参数,创建一个线程
2.public Thread(Runnable target,String name)
以Runnable接口对象作为参数,创建一个线程,并设置线程名称
代码实现:
//通过实现Runnable接口来创建线程
public class RunnableDemo implements Runnable{
//实现run()方法
public void run() {
System.out.println("线程名称:"+Thread.currentThread().getName());
System.out.println("我是通过实现Runnable接口来的线程");
}
public static void main(String[] args) {
RunnableDemo runnableDemo = new RunnableDemo();//创建Runnable接口对象
Thread thread = new Thread(runnableDemo,"线程111");//创建线程
thread.start();//启动线程
}
}
运行结果:
两种创建线程的特点和应用
1.继承Thread类的方式
1.操作简单
2.可以直接操作线程,适用于单继承的情况
3.多个线程分别完成自己的任务
2.实现Runnable接口的方式(推荐使用)
1.避免单继承的局限性:当一个线程继承了另一个类时,就只能通过该方法来创建线程
2.便于资源共享:可以使多个线程之间使用同一个Runnable对象
3.多个线程共同完成一个任务
采用实现Runnable接口来实现多线程(模拟用户抢票)
分析:
多个用户来进行抢票,总票数是固定的,多个用户同时来抢100张票,票数是共享的资源
代码实现:
//通过实现Runnable接口来创建线程
public class RunnableDemo implements Runnable{
private int ticket = 10;//总票数
private int num = 1;//第几张票
@Override
public void run() {
while (true){
if(ticket<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余票数:"+ticket);
num++;
ticket--;
}
}
}
//创建多个线程来抢票
public class User {
public static void main(String[] args) {
RunnableDemo runnableDemo = new RunnableDemo();//创建一个Runnable接口对象
//两个线程公用一个Runnable接口,实现资源共享
Thread thread1 = new Thread(runnableDemo,"淘票票");
Thread thread2 = new Thread(runnableDemo,"黄牛党");
thread1.start();
thread2.start();
}
}
运行结果:
从运行结果看出,多线程是交替占用CPU资源的,并且是个随机的过程,每次运行的结果不一样
线程有哪些状态
看一张图就明白了
线程调度
线程优先级
用1-10表示,10表示优先级最高,默认值是5,每个优先级对应一个Thread类的公共静态变量
public static final int NORM_PRIORITY = 5;
public static final int MIN_PRIORITY= 1;
public static final int MAX_PRIORITY = 10;
setPriority(int grade)
修改线程的优先级,必须是一个1-10的整数
优先级只是提高抢占CPU资源的可能性,不一定优先级高的就一定先抢到CPU资源
实现线程调度的常用方法
1.join()
使当前线程暂停执行,当前线程进入阻塞状态,等待调用该方法的线程结束后再继续执行本线程
2.public static void sleep(long millis)
使当前线程睡眠(停止执行)millis毫秒,由运行状态进入阻塞状态,睡眠时间过后进入就绪状态,等待运行
产生异常:InterruptException
3.public static void yield()
使当前线程暂停执行,允许其他线程执行,此时当前线程进入就绪状态,只是提供礼让的可能
如何解决多线程引发的数据不安全的问题
什么是数据不安全
回看前面的模拟用户抢票的运行结果,有以下一些问题:
1.抢到同一张票
2.剩余的票数结果不正确
产生的原因:
每个线程都是独立的,不会考虑其他线程的状态或行为,也就是说当前面线程还未执行完所有操作(如数据的更新),后续线程就开始抢占资源了,导致程序运行的结果可能不正确
那如何保证多线程的数据安全
实现线程同步
线程同步是指以某种顺序来确保资源在某一时刻只能被一个线程使用
采用同步来控制线程的执行,有同步方法和同步代码块两种解决办法,都使用synchronized关键字来实现
synchronized的作用
使得每个类实例对应一把锁,方法或代码块一旦执行,就独占该锁,直到该方法或代码块执行结束才将锁释放,这种机制使得所有声明synchronized的方法或代码块只能有一个处于可执行状态,有效避免访问冲突
1.同步方法
加入synchronized关键字来声明同步方法
代码实现:
public class RunnableDemo implements Runnable{
private int ticket = 1000;//总票数
private int num = 1;//第几张票
@Override
public void run() {
while (true){
bookTicket();
}
}
public synchronized void bookTicket(){
if(ticket>0){
ticket--;
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余票数:"+ticket);
num++;
}
}
}
2.同步代码块
加入synchronized关键字来声明同步代码块
代码实现:
public class RunnableDemo implements Runnable{
private int ticket = 10;//总票数
private int num = 1;//第几张票
@Override
public void run() {
while (true){
synchronized (this){
if(ticket==0){
break;
}
ticket--;
System.out.println(Thread.currentThread().getName()+"抢到了第"+num+"张票,剩余票数:"+ticket);
num++;
}
}
}
}
运行结果:
这样就可以保证程序运行结果的正确性