一.相关概念的理解
程序:一个固定的运行逻辑和数据等的集合,一般储存在硬盘中.
进程:一个正在运行的程序就是一个进程,进程是一个动态的概念
线程:多个线程组成一个程序,每个线程的都可以独立完成其中一个任务,而且各个任务之间没有依赖关系,都可以单独执行.
并行和并发:并行是指多个进程或线程同时运行,计算机cpu不能实现,并发是指cpu在多个线程之间高速切换,可以实现,并发示意图如下:
二.创建线程的两种常用方式
方式一:
创建一个类T,并继承Thread类,在T类中重写run()方法,
创建T对象t,t.Start();即可启动线程
public class Work1_2 {
public static void main(String[] args) {
//创建Window2类对象
Window2 t1 = new Window2();
//启动线程
t1.setName("赵新");
//创建Window2类对象
Window2 t2 = new Window2();
//启动线程
t2.setName("詹爱芳");
//创建Window2类对象
Window2 t3 = new Window2();
t3.setName("程文涛");
t1.start(); //启动线程
t2.start(); //启动线程
t3.start(); //启动线程
}
}
//创建一个类Window2,并继承Thread类,
class Window2 extends Thread {
public static int tkts = 100;
//重写run()方法
@Override
public void run() {
while (true) {
synchronized (Window2.class) {
if (tkts <= 0) {
System.out.println("已卖光,明天再来吧 ");
break;
}
System.out.println(getName() + "卖出了第" + tkts-- + "已经卖出去了,还剩" + tkts);
}
}
}
}
方式二:
创建一个类T,实现接口Runnable,在T类中重写run方法.
T t = new T();
Thread th = new Thread(t);
package com.youjiuye.work;
public class Work1 {
public static void main(String[] args) {
//
Window w1 = new Window();
Thread t1 = new Thread(w1);
//
Window w2 = new Window();
Thread t2 = new Thread(w2);
//
Window w3 = new Window();
Thread t3 = new Thread(w3);
t1.start(); //启动线程
t2.start(); //启动线程
t3.start(); //启动线程
}
}
//创建一个类Window 实现接口runnble
class Window implements Runnable {
public static int tkt = 1000;
//重写run方法
@Override
public void run() {
synchronized (Window.class) {
while (true) {
if (tkt <= 0) {
System.out.println("诶嘿,卖光了.");
break;
}
System.out.println("第" + tkt-- + "张票已卖出,还剩" + tkt);
}
}
}
}
三.使用匿名内部类创建线程对象
public class 匿名内部类 {
public static void main(String[] args) {
//用匿名内部类创建继承方式的线程对象
Thread t1 = new Thread() {
@Override
public void run() {
//这里写线程要执行的代码
}
};
//用匿名内部类创建实现接口方式的任务对象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//这里写线程要执行的代码
}
});
t1.start();
t2.start();
}
}
四.线程的常用方法
获取线程名字:
getName();
1.可以使用线程对象.这个方法. 也可以在线程类中直接使用getName();.
在这个线程类被调用时,获取的就是调用这个线程类的对象的线程的线程名.
2.实现了Runnable接口的类, run方法中不能使用getName().
3.没有给线程起名字,那么线程有默认的名字,名字就是Thread-x, x序号,从0开始.
设置线程名程:
1.setName(String name); 使用线程对象调用setName(String name);修改线程名,
可以在线程开启钱修改也可以在线程开启后修改.
2.Tread的构造方法:
new Tread(String name);
new Tread(Runnable r, String name);
获取当前线程对象
Thread.currentThread();获取这句话所在线程的线程对象.
线程中的休眠方法
当某段代码在运行到某处需要休眠时,就在某处加上Thread.sleep(long 毫秒);
Thread.sleep();方法有一个编译时异常,使用时要处理:
1.普通方法中既可以生命也可以捕获.
2.在继承了Tread的方法中只能捕获
3.在实现了runnable 的方法中也只能捕获
五.守护线程
守护线程就是用于守护其他线程可以正常执行的线程,为其他核心线程准备良好的运行环境.
如果非守护线程全部死亡(挂起), 那么守护线程就没有存在意义的,一段时间之后,
守护线程也会死亡(挂起);
setDaemon(boolean flag): 线程对象调用这个方法,参数给true,此时这个线程就是守护线程;
参数给false,它就不是守护线程.
boolean isDaemon(): 测试一个线程对象是否为守护线程,是就返回true, 否则返回false;
六.锁
某段代码在某个线程中执行, 但是这段代码还没有完整的执行结束, CPU就把资源切换其他线程中,执行别的任务了,此时就会导致一些错误的数据产生.所以我们使用锁对象(synchronized)来保证某段代码执行完后CPU在切换走到其他线程.
同步代码块
synchronized ( 锁对象 ){
需要保证执行不对打断的代码
}
同步方法
如果一个方法中所有的代码都需要用同步代码块包裹,那么我们就可以把这个方法简写成同步方法.
权限修饰符 [静态修饰符] synchronized 返回值类型 方法名(参数列表){
}
练习:卖火车票案例
一共有100张票, 三个窗口(安锋, 江硕, 淑芬)卖票,要求不能卖出重复的票,也不能少卖,也不能多卖; 提示: 三个窗口就相当于三个线程,操作同一个公共的资源.
在这里插入代码片
七.线程的生命周期
线程状态:
新建态:刚创建对象的时候
就绪态:线程准备好了所有运行的资源,只差cpu来临
运行态:cpu正在执行.
阻塞态:线程主动休息或者缺少一些运行的资源,即使cpu来临也无法运行
死亡态:运行完成/出现异常/调用方法结束
Java中通过代码的方式获取线程的状态描述
getState()方法,获取线程各种状态,返回值是Thread.State ,是Thread的内部类.
NEW 新建态,没有开启线程
RUNNABLE 运行态
BLOCKED 阻塞态(锁,I/O)
WAITING 阻塞态(wait方法)
TIME_WAITING 阻塞态(sleep方法)
TERMINATED 死亡态
//代码演示
package com.youjiuye.prictice;
public class 线程状态获取_01 {
private static final String A = "A";
private static final String B = "B";
public static void main(String[] args) {
test_获取线程TIMED_WAINTING状态();
}
private static void test_获取线程的TERMINATED状态() {
Thread t = new Thread() {
public void run() {
System.out.println("祥哥好帅");
};
};
t.start();
while(true) {
System.out.println(t.getState());// TERMINATED
}
}
private static void test_获取线程BLOCKED状态() {
Thread t1 = new Thread() {
@Override
public void run() {
synchronized (A) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (B) {
System.out.println("任务1....");
}
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
synchronized (B) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (A) {
System.out.println("任务2....");
}
}
}
};
t1.start();
t2.start();
while(true) {
System.out.println(t1.getState());// BLOCKED
}
}
private static void test_获取线程WAITING状态() {
Object lock = new Object();
Thread t = new Thread() {
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
};
t.start();
while(true) {
System.out.println(t.getState());// WAITING
}
}
private static void test_获取线程TIMED_WAINTING状态() {
Thread t = new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t.start();
// 在主线程获取t线程的状态
while(true) {
System.out.println(t.getState()); // TIMED_WAINTING
}
}
private static void test_获取线程NEW和RUNNABLE状态() {
Thread t = new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
// 获取线程状态
System.out.println(this.getState());
}
}
};
System.out.println(t.getState()); // NEW
t.start(); // RUNNABLE
}
}
八.线程池
在没有任务的时候, 会先把一些线程对象提前准备好, 存储到一个 线程池 中,当一旦有了任务,
不需要创建线程了,直接从线程池中获取一条线程,来执行这个任务. 当任务正常执行结束, 不会把线程对象销毁,
而是重新放回线程池;等待新的任务. 当任务破坏力比较大, 导致这条线程对象被挂起, 此时任务不会终止,
而是继续去线程池中获取一条线程,继续执行之后, 直到把任务执行完为止.再把线程对象放回线程池; 使用线程池的缺点:
不管有没有任务,都需要维护一部分线程对象,这部分就会一直占着系统资源. 但是好处远远大于缺点. 能使用线城池就使用;
使用步骤:
①创建线程池对象 --> ②创建任务对象:Runnable的实现类 --> ③把任务提交到线程池中 --> ④关闭线程池
package com.ujiuye.threadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class 线程池的使用_01 {
public static void main(String[] args) {
// 1 获取一个拥有三条线程的线程池对象
ExecutorService pool = Executors.newFixedThreadPool(3);
// 2创建四个任务对象
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---任务1");
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---任务2");
}
};
Runnable r3 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---任务3");
}
};
Runnable r4 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---任务4");
}
};
// 3.提交任务
pool.submit(r1);
pool.submit(r2);
pool.submit(r3);
pool.submit(r4);
// 4关闭线程池
// pool.shutdown();
pool.shutdownNow();
//pool.submit(r1);
}
}