一、基本概念
进程
程序在运行时的一个实例,在任何时间点上,只能有一个进程在运行,由于进程切换速度快,人类无法察觉,宏观并行,微观串行。
线程
轻量级的进程,一个进程中可以包含多个线程,每个线程可以完成不同的工作,交替执行。
CPU调度时的基本单位
二、线程的创建和使用
2.1、线程的创建
a)、继承Thread
/**
* 1、创建一个类继承Thread类
* 2、覆盖Thread的run方法
* 3、创建该类的对象
* 4、调用start()方法
**/
class MyThread extends Thread{
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0) System.out.println(i);
}
}
}
public class Test {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
附:Thread类中的部分方法使用
* start():启动一个线程中的run()方法
* run(): 覆盖Thread中的run方法,所要执行的操作
* currentThread(): 静态方法,返回执行当前代码的线程
* getName():获取线程的名字
* setName():设置当前线程的名字
* yield():释放cpu的执行权
* join():在线程a中调用线程b的join,a会进入阻塞状态,直至b线程执行完毕,a线程才会被释放
* sleep():设置线程睡眠时间
* isAlive():判断线程是否存活
*
* 线程优先级
* MIN_PRIORITY:1
* NORM_PRIORITY:5
* MAX_PRIORITY:10
* 线程优先级
* getpriority():获取
* setpriority():设置
卖票问题
/**
* @program: javademo
* @description: 卖票问题(会出现重复票号)
* @author: Tang
* @create: 2019-10-08 15:05
**/
class Window extends Thread{
private static int ticket = 100;
@Override
public void run() {
while (true){
if(ticket > 0){
System.out.println(getName()+"成功出售"+ticket+"号");
ticket--;
}else break;
}
}
}
public class TestWindow {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.start();
w2.start();
w3.start();
}
}
b)、实现Runnable接口
class MineThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0) System.out.println(i);
}
}
}
public class TestRunable {
public static void main(String[] args) {
MineThread mineThread = new MineThread();
Thread t1 = new Thread(mineThread);
t1.start();
}
}
两种方式的比较
1.实现Runnable接口不存在继承Thread类的局限性(java单继承)。
2.实现的方式更适合多个线程共享数据的情况
三、线程的生命周期
四、线程的同步
同步代码块
语法:synchronized(临界资源){ //只有得到临界资源锁标记的线程,方可执行 //原子操作 } //当同步代码块执行完毕后,会释放掉该临界资源的所标记 //其他线程竞争该锁标记
同步方法
语法:访问权限修饰符 synchronized 返回值类型 方法名(参数表)抛出的异常{ //当前对象(this)作为锁标记 //当多个线程对同一个对象调用该方法时,只能有一个线程得到锁标记 //其他线程在当前对象的锁池中 等待锁标记 }
五、线程的通信
涉及方法
wait():让当前线程进入阻塞状态,并释放同步监视器。
notify():执行此方法会唤醒被wait的线程,此时如果有多个wait的线程,则会唤醒优先级高的线程。
notifyAll():唤醒所有被wait()的线程
/**
* @description: 两个线程交替打印1~100
**/
class TestRunnablee implements Runnable{
private int start = 1;
@Override
public void run() {
while (true){
synchronized(this){
this.notify();
if(start<=100){
System.out.println(Thread.currentThread().getName()+":"+start);
start++;
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else break;
}
}
}
}
public class TestPlus {
public static void main(String[] args) {
TestRunnablee testRunnablee = new TestRunnablee();
// 创建线程对象
Thread t1 = new Thread(testRunnablee);
Thread t2 = new Thread(testRunnablee);
t1.setName("线程一");
t2.setName("线程二");
t1.start();
t2.start();
}
}
面试题
sleep() 和wait()的区别?
相同:二者都会使线程进入阻塞状态
不同:sleep不会释放锁,会自动打开阻塞。
wait()需要手动打开阻塞状态
经典案例:生产者消费者
/**
* @description: 生产者消费者案例
**/
class Phone{
private int phoneNum = 0;
public synchronized void add(){
if(phoneNum <100){
phoneNum++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"生产了"+phoneNum+"部手机");
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void reduce(){
if(phoneNum>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"购买了"+phoneNum+"部手机");
phoneNum--;
notify();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 生产者
class Producer implements Runnable{
private Phone phone;
public Producer(Phone phone) {
this.phone = phone;
}
@Override
public void run() {
while (true){
phone.add();
}
}
}
// 消费者
class Consumer implements Runnable{
private Phone phone;
public Consumer(Phone phone) {
this.phone = phone;
}
@Override
public void run() {
while (true) {
phone.reduce();
}
}
}
public class ProducerConsumer {
public static void main(String[] args) {
Phone phone = new Phone();
Producer producer = new Producer(phone);
Consumer consumer = new Consumer(phone);
Thread p = new Thread(producer);
p.setName("生产者");
Thread c = new Thread(consumer);
c.setName("消费者");
p.start();
c.start();
}
}
六、JDK5.0新增线程的创建方式
实现Callable接口
class ICallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int num = 0;
for (int i = 0; i < 100; i++) {
if(i % 2 == 0){
System.out.println(i);
num+=i;
}
}
return num;
}
}
public class TestCallable {
public static void main(String[] args) {
ICallable iCallable = new ICallable();
FutureTask<Integer> futureTask = new FutureTask<Integer>(iCallable);
new Thread(futureTask).start();
try {
Integer value = futureTask.get();
System.out.println(value+"========================");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
线程池
class Runnable1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2 == 0) System.out.println(i);
}
}
}
public class ThreadPoll {
public static void main(String[] args) {
// 创建一个容量为10的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 启动该线程
executorService.execute(new Runnable1());
// 归还线程
executorService.shutdown();
}
}