目录
一、概念
1、程序、进程、线程
二、线程的创建和使用
1、通过继承Thread类的方式
2、通过实现Runnable接口的方式:
3、Thread类中的相关方法
4、线程优先级
三、线程的生命周期
1、线程的几种状态
四、线程的同步
1、同步代码块
2、同步方法
一、概念
1、程序、进程、线程
程序:是为了完成某个任务,利用某种语言编写的一组指令的集合,及指一块静态的代码。
进程:是程序的一次执行的过程,也可以是一个正在运行的程序,是一个动态的过程,有他自己产生、运行、消亡的过程-----------即生命周期。
线程:进程可以进一步细化为线程,程序在运行中,内部的一条执行路径。若一个程序内部并行执行了多条路径,即为多线程。
而线程作为调度和执行的单位,拥有独立的运行栈和程序技术器,一个进程内的多个线程共享相同的内存单元和内存地址空间,从同一个堆中分配对象,访问相同的对象和变量。但是多个线程操作共享的变量和对象,会带来安全隐患
二、线程的创建和使用
1、通过继承Thread类的方式
步骤:
①:创建一个Thread的子类
②:重写Thrad类中run()方法,将执行此线程的操作声明在run()方法中
③:实例化当前子类的对象
④:调用Thread类中的start方法
//创建Thread子类
class MyThread extends Thread {
//重写Thread类中run方法
public void run() {
//将此线程执行的操作声明在run方法中
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.print(Thread.currentThread().getName() + ":" + i);//输出当前线程的线程名和i
}
}
}
}
public class ThreadTest12 {
public static void main(String[] args) {
//创建Thread子类的对象
MyThread mt = new MyThread();
//调用start方法:1、启动当前线程;2、调用当前线程的run()方法
mt.start();
}
}
这种方式有两个问题:
①、我们不能通过直接调用run()方法来启动线程
②、再启动一个线程,不能让已经执行start()方法的线程去执行,否则会报错:
IllegalThreadStateException
2、通过实现Runnable接口的方式:
步骤:
①:创建一个实现了Runnable接口的类
②:重写Runnable类中的抽象方法:run()
③:创建此实现类的对象
④:将此实现类的对象作为参数传递到Thrad类的构造器中,创建Thread类的对象
⑤:调用Thread类中的start方法
class MyThread implements Runnable {
public void run() {
//将此线程执行的操作声明在run方法中
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.print(Thread.currentThread().getName() + ":" + i);//输出当前线程的线程名和i
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//创建实现类的对象
MyThread mt = new MyThread();
//将此实现类的对象作为参数传递到THread构造其中,创建Thread类的对象
Thread ta = new Thread(mt);//体现了多态性
//调用Thread类中的start()方法
ta.start();
Thread ta1 = new Thread(mt);//体现了多态性
ta1.start();//再启动一个线程,执行run方法,注意:此时两条线程操作同一个对象
}
}
总结:比较两种线程创建的方式:
1、在开发中优先使用:实现Runnable接口的方式
原因:
①:实现的方式没有类的单继承的局限性
②:实现的方式更适合处理多线程有共享数据的情况
两者之间的练习:
①:Thread类本身也是实现了Runnable接口中的run方法
②:都需要重写run方法
③:都要将线程需要执行的操作声明在run方法中
3、Thread类中的相关方法
void start():启动线程,并执行对象的run()方法
run():线程再被调度是执行的操作
String getName():返回当前对象的名称
void setName(String name):设置当前线程的名称
static Thread currentThread():返回当前线程,在Thread子类中就是this,通常用于主线程和Runnable实现类
static void yield():线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
若队列中没有同优先级的线程,则忽略此方法
join():当某个程序执行流中,调用其他线程的join方法时,调用线程将被阻塞,直到join方法加入的join方法执行完为止
static void sleep(long millis)(指定时间:毫秒):
①:令当前活动线程在指定时间内放弃对cpu的控制,使其他线程有机会被执行,时间到后重新排队
②:抛出InterruptedException异常
stop():强制结束线程声明周期,不推荐使用
boolean isAlive:判断线程是否还活着
4、线程优先级
同优先级线程组成先进先出队列(先到先服务),使用时间片策略
对优先级线程,采用优先调度的抢占式策略(高优先级的线程抢占CPU)
线程的优先级等级:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
涉及的相关方法:
getPriority()返回相关线程的优先级登记
setPriority(int newPriority)设置相关线程的优先级登记
注意点:
线程创建时继承父线程的优先级
低优先级的只是获得调度的概率低,并非一定是高优先级完成后才能调度
三、线程的生命周期
1、线程的几种状态
四、线程的同步
前序:当某个线程在执行的过程中,尚未完成操作时, 其他线程参与进来,会导致线程安全问题,以下两种方式可以解决(思路:当一个线程在操作共享数据时,让其他线程不能参与进来,直到线程a操作完,其他线程才能进来,这样即使线程a发生阻塞,也不能被改变)
1、同步代码块
synchronized(同步监视器){ //同步监视器:俗称:锁,任何一个类的对象都可以称当锁,要求多个线程必须共用一把锁!
//需要被同步的代码
}
①:继承Thread子类的方式结合同步代码块
//创建Thread子类
class MyThread extends Thread {
//重写Thread类中run方法
public void run() {
synchroinzed(this){ //可以使用当前对象,不过继承Thread类的方式慎用this
//将此线程执行的操作声明在run方法中
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.print(Thread.currentThread().getName() + ":" + i);//输出当前线程的线程名和i
}
}
}
}
}
public class ThreadTest12 {
public static void main(String[] args) {
//创建Thread子类的对象
MyThread mt = new MyThread();
//调用start方法:1、启动当前线程;2、调用当前线程的run()方法
mt.start();
}
}
②:实现Runnable接口的方式结合同步代码块
class Window1 implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true){
synchronized(this) { //使用当前对象也可以,此时的this:唯一的window1的对象
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread t =new Thread(w);
Thread t1 =new Thread(w);
Thread t2=new Thread(w);
t.start();
t1.start();
t2.start();
}
}
2、同步方法
①:通过实现Runnable接口的方式结合同步方法
//使用同步方法解决实现Runnable接口的线程安全问题
class Window3 implements Runnable {
private int ticket = 1000;
@Override
public void run() {
while (true) {
show();
}
}
public synchronized void show() { //同步监视器:this
// synchronized (this) {
if (ticket > 0) {
/* try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}
// }
}
}
public class WindowTest3 {
public static void main(String[] args) {
Window3 w = new Window3();
Thread t = new Thread(w);
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
t.start();
t1.start();
t2.start();
}
}
②:通过继承Thread类的方式结合同步方法
/**使用同步方法处理继承Thread类的方式中的线程安全问题
*
* 关于同步方法的总结:
* 1、同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
* 2、非静态的同步方法,同步监视器是:this
* 静态的同步方法:同步监视器是当前类本身
*
*/
class Window4 extends Thread {
private static int ticket = 100;
//变成唯一,使用static
@Override
public void run() {
while (true) {
show();
}
}
private static synchronized void show() {//同步监视器:Window.class
//private synchronized void show()//这种方法是错的,进来的是三个对象
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
ticket--;
}
}
}
public class WindowTest4 {
public static void main(String[] args) {
Window4 w = new Window4();
Window4 w1 = new Window4();
Window4 w2 = new Window4();
w.setName("窗口1");
w1.setName("窗口2");
w2.setName("窗口3");
w.start();
w1.start();
w2.start();
}
}