(一)多线程
基本概念
-
并行:同时进行
并发:交替进行 -
硬盘:永久存储
内存:应用程序都要进入内存中执行 临时存储RAM -
进程:进入内存的程序
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。 一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
thread多线程原理
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() :返回对当前正在执行的线程对象的引用。
创建新线程的方法一
通过继承Thread类来创建并启动多线程的步骤如下:
1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任
务,因此把run()方法称为线程执行体。
2. 创建Thread子类的实例,即创建了线程对象
3. 调用线程对象的start()方法来启动该线程
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("run"+i);
}
}
}
MyThread mt=new MyThread();
mt.start();
for (int i = 0; i < 10; i++) {
System.out.println("main"+i);
}
创建新线程的方法二
1.创建Runnable的实现类,重写run()方法
2.创建Runnable实现类对象
3.创建Thread对象,构造方法中传递Runnable的实现类对象
4.调用Thread类中的start()方法,开启新线程执行run()方法
public class RunnableImpl implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
RunnableImpl runnable=new RunnableImpl();
Thread t=new Thread(runnable);
t.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
两者区别
实现Runnable接口创建多线程程序的好处
1.避免了单继承的局限性
一个类只能继承一个类,类继承了Thread类就不能继承其他的类
实现了Runnable接口,还可以实现其他的接口
2.增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnanle接口的方式,把设置线程任务和开启新线程进行了分离(解耦):实现类中,重写了run()方法,用来设置线程任务;创建Thread对象,调用start()方法,来开启新线程
匿名内部类实现线程的创建
//Tread类方式
new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
//Runnable接口的方式
Runnable rb=new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
};
new Thread(rb).start();
//简化代码
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}).start();
(二)线程安全
1.线程同步三种解决方式
1.同步代码块。
2.同步方法。
3.锁机制。
2.同步代码块
Object obj=new Object();//创建锁对象
synchronized ( 同步锁obj ) {
需要同步操作的代码
}
1.锁对象可以是任意类型。
2.多个线程对象要使用同一把锁。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED)。
·当一个线程进入代码块时,拿到所对象方可执行代码块内容,执行完代码块内容归还锁对象;若锁对象不在,则须等待上个线程归还锁对象并拿到锁对象才可进入代码块。此举实现同步。
3.锁机制
·Lock 接口:
void lock();加同步锁
void unlock();释放同步锁
Reentrantlock():Lock接口的实现类对象
4.同步方法
public synchronized void method(){
可能出现问题的代码
}
·非静态方法,同步锁就是this
`静态方法,同步锁就是当前方法所在类的字节码对象(类.class)
(三)线程状态
NEW(新建) 线程刚被创建,但是并未启动。还没调用start方法。
Runnable(可运行) 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
Blocked(锁阻塞) 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待) 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TimedWaiting(计时等待) 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法Thread.sleep 、Object.wait。
Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
- Timed Waiting在API中的描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。
其实当我们调用了sleep方法之后,当前执行的线程就进入到“休眠状态”,其实就是所谓的Timed Waiting(计时等待)
sleep方法的使用还是很简单的。我们需要记住下面几点:
- 进入 TIMED_WAITING 状态的一种常见情形是调用的 sleep 方法,单独的线程也可以调用,不一定非要有协 作关系。
- 为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程 中会睡眠
- sleep与锁无关,线程睡眠到期自动苏醒,并返回到Runnable(可运行)状态。 小提示:sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就 开始立刻执行。
- Blocked状态在API中的介绍为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态。
比如,线程A与线程B代码中使用同一锁,如果线程A获取到锁,线程A进入到Runnable状态,那么线程B就进入到Blocked锁阻塞状态。 - Wating状态在API中介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态。
`
买包子示例:顾客告诉老板买什么包子,等待等老板做包子;老板做好之后,顾客拿走自己的包子。
创建顾客线程和老板线程,等待即顾客这个线程保持waiting状态,老板线程执行,执行完后调用notify()唤醒waiting状态。
Object obj =new Object();
//顾客线程
new Thread(){
@Override
public void run() {
//同步
synchronized (obj){
System.out.println("顾客下单包子的数量和种类");
try {
obj.wait();//等待机制
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("顾客拿到自己的包子");
}
}
}.start();
//老板线程
new Thread(){
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("您的包子好了!小心烫~");
obj.notify();//唤醒机制
}
}
}.start();