1.定义:
线程是进程的一个单元(进程就是启动一个应用程序)
2.实现方式:
继承Thread类:
public class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(name+":"+"下载了"+i+"%");
}
}
}
==================================================
public class Test {
public static void main(String[] args) {
MyThread mt = new MyThread("海王");
mt.start();
MyThread mt1 = new MyThread("荔湾");
mt1.start();
}
}
使用Runnable接口:
public class DownloadMovies implements Runnable {
private String name;
public DownloadMovies(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(name+":"+"下载了"+i+"%");
}
}
}
===================================================
public class Test {
public static void main(String[] args) {
DownloadMovies dm = new DownloadMovies("小鬼当家");
new Thread(dm).start();
DownloadMovies dm1 = new DownloadMovies("家有儿女");
new Thread(dm1).start();
Thread t = new Thread(new DownloadMovies("海王"));
t.start();
}
}
3.线程的生命周期
- 新建:线程被new出来
- 准备就绪:线程具有执行的资格,即线程调用了start(),没有执行的权力
- 运行:具备执行的资格,具备执行的权力
- 阻塞:没有执行的资格和执行的权力
- 销毁:线程对象变成垃圾,释放资源(sleep(),wait())
4.线程的并发(安全问题)
实例:车票4个窗口售卖100张票,100张票是共享资源,4个窗口是4个线程同时工作.
解决:加锁synchroized()
格式:synchroized(加锁对象)
{
//操作共享资源的代码
}
同步代码加在什么地方?
a.代码被多个线程访问
b.代码中有共享的数据
c.共享数据被多条语句操作
例:
public class SaleTicket extends Thread {
private String name;
static int ticket = 100;
public SaleTicket(String name) {
this.name = name;
}
//创建一个锁对象,这个对象是多线程对象所共享的数据
static Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj)
{
if (ticket > 0) {
System.out.println(name + ":卖出的座位是:" + (ticket--) + "号");
} else {
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(name+"卖票结束!");
}
}
====================================================
public class TestTicket {
public static void main(String[] args) {
SaleTicket sa = new SaleTicket("窗口1");
sa.start();
SaleTicket sa1 = new SaleTicket("窗口2");
sa1.start();
SaleTicket sa2 = new SaleTicket("窗口3");
sa2.start();
SaleTicket sa3 = new SaleTicket("窗口4");
sa3.start();
}
}
同步方法:
public class SaleTicket extends Thread {
private String name;
static int ticket = 100;
public SaleTicket(String name) {
this.name = name;
}
static Object obj = new Object();
@Override
public void run() {
while (true) {
if (saleTicket()) {
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"没票啦!");
}
public synchronized boolean saleTicket(){
boolean isFinish = false;
if (ticket > 0) {
System.out.println(name + ":卖出的座位是:" + (ticket--) + "号");
} else {
isFinish = true;
}
return isFinish;
}
}
静态同步方法:锁对象不能是this,是本类的class文件
Lock锁:实现了synchronized方法和语句获得更广泛的锁定操作
方法:
void lock() 获取锁
void unlock() 释放锁
使用步骤:
在成员位置创建一个Reentrantlock对象
在可能出现的安全问题的代码前调用Lock接口中的方法Lock获取锁
在可能出现的安全问题的代码后调用Lock接口中的方法unlock释放锁
例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicket extends Thread {
private String name;
Lock lock = new ReentrantLock();
static int ticket = 100;
public SaleTicket(String name) {
this.name = name;
}
static Object obj = new Object();
@Override
public void run() {
while (true) {
lock.lock();
if (ticket > 0) {
System.out.println(name + ":卖出的座位是:" + (ticket--) + "号");
} else {
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
System.out.println(name + "没票啦!");
}
}
================================================
public class TestTicket {
public static void main(String[] args) {
SaleTicket sa = new SaleTicket("窗口1");
sa.start();
SaleTicket sa1 = new SaleTicket("窗口2");
sa1.start();
SaleTicket sa2 = new SaleTicket("窗口3");
sa2.start();
SaleTicket sa3 = new SaleTicket("窗口4");
sa3.start();
}
}
5.休眠
在做服务端的程序的时候都需要给一个休眠时间,在synchronized代码块中不会释放cpu,会一直占用cpu的资源.
6.线程间通信(等待与唤醒机制,有效利用资源)
多个线程在处理同一个任务,但是处理的动作(线程的任务)却不相同.
wait(); 休眠线程
notify(); 唤醒等待队列中休眠的线程
notifyAll(); 唤醒等待队列的休眠的所有线程
7.线程池(容器)
Executors:线程池的工厂类,用来生成线程池
Executors类中的静态方法:
static ExecutorService newFiedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池
参数:int nThreads:创建线程池中包含的线程数量
返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象
submit(Runnable)
用来从线程池中获取线程,调用start方法,执行线程任务
void shutdown()
关闭销毁线程池的方法
线程池的使用步骤:
- a.使用线程池工厂类Executors方法生产一个线程池
- b.创建一个类,实现Runnable接口,重写run方法
- c.调用ExecutorSevic接口的submit方法,开启线程,执行run方法
- d.调用shutdown方法销毁关闭线程
例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
}
}
===========================================================
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行");
}
}
8.Lambda表达式(函数式编程简化代码)
使用前提:必须有接口且接口中有且只有一个抽象方法.必须具有上下文推断(方法的参数或局部变量必须为Lambda对应的接口类型)
标准格式:
- a.一些参数
- b.一个箭头
- c.一段代码
- (参数列表) -> {一些重写的方法代码}
- ():接口中抽象方法的参数列表,没有参数就空着,多个参数逗号分隔
- ->:把参数传递给方法体
- {}:重写的方法代码
例:
public class DemoCook {
public static void main(String[] args) {
//匿名内部类
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("吃饭了");
}
});
//Lambda表达式
invokeCook(()->{
System.out.println("吃饭了!");
});
}
private static void invokeCook (Cook cook)
{
cook.makeFood();
}
}
============================================
public interface Cook {
public abstract void makeFood();
}