一、多线程是什么?
介绍多线程之前要介绍线程,介绍线程则离不开进程。
首先进程 :是一个正在执行中的程序,例如正在运行的QQ、 微信,等。
线程:就是进程中的一个独立控制单元,线程在控制着进程的执行。一个进程中至少有一个进程。
多线程:一个进程中不只有一个线程,换句话说,一个进程有很多噶线程。
二、为什么要用多线程?
①、为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待;
②、进程之间不能共享数据,线程可以之间可以共享数据;
③、系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小;
④、Java语言内置了多线程功能支持,简化了java多线程编程。
三、线程的生命周期:
•新建 :从新建一个线程对象到程序start() 这个线程之间的状态,都是新建状态;
•就绪 :线程对象调用start()方法后,就处于就绪状态,等到JVM里的线程调度器的调度;
•运行 :就绪状态下的线程在获取CPU资源后就可以执行run(),此时的线程便处于运行状态,运行状态的线程可变为就绪、阻塞及死亡三种状态。
•等待/阻塞/睡眠 :在一个线程执行了sleep(睡眠)、suspend(挂起)等方法后会失去所占有的资源,从而进入阻塞状态,在睡眠结束后可重新进入就绪状态。
•终止 :run()方法完成后或发生其他终止条件时就会切换到终止状态。

四、主线程和子线程
main里面的代码是在主线程里面执行的,主线程的名称main,每个进程都有一个主线程,运行起来就要执行的线程,就是主线程
五、创建线程的方法:
一、创建线程必须掌握的两种方法
1、继承Thread类:
实现继承Thread类创建线程步骤如下:
- ①、定义类继承Thread;
- ②、重写Thread类中的run方法;
目的:将自定义代码存储在run方法,让线程运行- ③、调用线程的start方法:
该方法有两步:启动线程,调用run方法。
实现:
1,继承类Thread并重写run方法
class TestThread extends Thread{
//2.实现run方法
@Override
public void run() {
String name = Thread.currentThread ().getName();
for(int i = 0; i <=100;i++){
System.out.println(name + " :" + i)'
}
super.run();
}
}
3.创建一个Thread对象调用线程的start方法
public class MyClass {
public static void main(String[] args){
TeatThread thread = new TestThread();
thread.start();
}
}
2,实现Runnable接口方式创建线程
实现Runnable接口创建线程类步骤如下:
- 1.创建实现Runnable接口的实现类
- 2.重写Runnable接口的run()方法
- 3.创建实现类的线程对象
- 4.将线程对象作为Thread的target来创建Thread对象
- 5.启动start()方法
1.创建实现Runnable接口的实现类并重写Runnable接口的run()方法如下:
class Test implements Runnable{
@Override
public void run(){
for (int i = 0; i < 6; i++) {
System.out.println(i);
}
}
}
2.创建实现类的线程对象
Test test = new Test();
3.将线程对象作为Thread的target来创建Thread对象
Thread thread = new Thread(test);
4.启动start()方法
thread.start();
2.3.4合在一起如下:
public class MyClass {
public static void main(String[] args){
Test test = new Test();
Thread thread = new Thread(test);
thread.start();
}
}
使用二:
Thread t = new Thread(new Runnable(){
@Override
public void run(){
for (int i = 0; i < 6; i++) {
System.out.println(i);
}
}
});
t.setName("子线程2");
t.start();
使用三,创建线程的同时,直接开启线程任务:
Thread t = new Thread(new Runnable(){
@Override
public void run(){
for (int i = 0; i < 6; i++) {
System.out.println(i);
}
}
}).start();
使用方式四:使用Lambda表达式,但是不建议使用这种方式,阅读性差
new Thread(()->{
for (int i = 0; i < 6; i++) {
System.out.println(i);
}).start();
其中可能用到的方法如下:

六、线程安全
当多个线程操作同一个资源,线程无法确定自己什么时候被阻塞,容易导致数据错误,即脏数据。
1.使⽤用synchronized,
1 .同步代码块 :
synchronized(监听器/锁/对象){
需要同步的代码
}
2,同步方法
- Lock锁机制, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块
练习 线程安全:
火车站卖票:
class ticket implements Runnable{
//定义所有车票的数量
public static int nums =100;
String name;
public ticket(String name){
this.name = name;
}
static final Object obj =new Object(); //同一把锁,可以创建任意的对象
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//参数就是监听器
synchronized (obj){
//同步代码啊块
if (nums > 0) {
System.out.println(name + "出票:" +nums);
nums--;
try {
obj.wait(300); //等待
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
obj.notify();//唤醒其他线程,其他线程来执行
}
}else {
break;
}
}
}
}
}
主函数调用
public class noon {
public static void main(String[]args){
ticket t = new ticket("重庆西站");
Thread T1 = new Thread(t);
T1.start();
ticket t1 = new ticket("北京站");
Thread T2 = new Thread(t1);
T2.start();
ticket t2 = new ticket("上海站");
Thread T3 = new Thread(t2);
T3.start();
}
}
运行结果部分截图:

网友评论