一、线程、进程
1.概念
- 进程是程序一次执行的过程,或是正在运行中的程序,是静态的概念
- 线程是一个任务从头到尾的执行过程,是动态的概念
- 一个进程同一时间并行执行多个线程,就是支持多线程的
- 并行和并发:并行是多个CPU同时执行多个任务,并发是一个CPU执行多个任务
2.线程的创建
三种:
方法一:继承Thread类
步骤:
- 定义子类继承Thread,使其成为线程类
- 子类中重写Thread类中的run方法
- 创建子类的对象,再调用start方法启动线程
源码分析:
package com.tang.dem01;
/**
* @author tanglei
* @create 2021-05-09 8:35
* 创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
* 线程开启不一定就立即开始执行,由CPU调度执行
*/
public class TestThread1 extends Thread{//第一步继承
@Override
public void run() {//第二步重写run
//run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("副线程---");
}
}
public static void main(String[] args) {
//主线程
TestThread1 testThread1 = new TestThread1();
testThread1.start();//第三步:创建对象调用start
for (int i = 0; i < 20; i++) {
System.out.println("主线程" + i);
}
}
}
方法二:实现Runnable接口
步骤:
- 实现Runnable接口
- 重写run方法,在run里面执行线程
- 在主函数里创建接口实现类的对象
- 再在主函数里面创建线程Thread对象,将接口类的实现对象传入进去
- 调用start方法
源码分析:
package com.tang.dem01;
/**
* @author tanglei
* @create 2021-05-09 9:00
*实现接口,重写run,丢入接口实现类,调用start方法
*/
public class TestThread2 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 1000; i++) {
System.out.println("副线程---");
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread2 testThread2 = new TestThread2();
//创建线程对象,通过线程对象来开启线程
Thread thread = new Thread(testThread2);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程" + i);
}
}
}
方法三:实现Callable接口
步骤:
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象(到此为止,和前面两种方法差不多)
- 创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(n);//这里的n表示线程数目
- 提交执行:
Future<Boolean> result1 = ser.submit(t1);
- 获取结果:
boolean r1 = result1.get();
- 关闭服务:
ser.shutdownNow();
3.前两种方法创建线程的区别
- 继承Thread是将线程代码存放在Thread子类的run方法中
- 实现Runnable是存放在接口的子类的run方法中
- 通常采用实现Runnable的方式,这样可以避免单继承的局限性,多个线程可以共享一个接口实现类的对象,适合多个相同线程来处理同一份资源
4.龟兔赛跑实例------巩固多线程
思路:
- 首先要一个赛道,离终点越来越近,for循环累加实现
- 创建判断比赛是否结束方法,通过步数判定,打印出胜利者
- 通过延迟来模拟兔子睡觉
源码分析:
package com.tang.dem01;
/**
* @author tanglei
* @create 2021-05-09 9:21
*/
public class Race implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+ "---跑了" + i + "步");
}
}
//判断是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if(winner != null){
return true;
}else if(steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("winner is "+ winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
5.静态代理
方法:
- 定义一个接口
- 被代理类去实现该接口
- 代理类去实现该接口,定义被代理类对象,构造函数传参
- 在被代理类实现该接口的前后可以加入方法
- 在主函数中创建代理类对象,再将被代理类作为参数传入代理类,调用实现接口的方法
特点:被代理(真实)对象和代理对象都要实现同一个接口,将被代理对象作为参数传入代理类。
源源码分析:
package com.tang.dem02;
/**
* @author tanglei
* @create 2021-05-09 9:59
*/
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//被代理类
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("要结婚了");
}
}
//代理类
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void after() {
System.out.println("结婚之后");
}
private void before() {
System.out.println("结婚之前");
}
}
结果:
6. Lamda表达式
函数式接口:任何接口,如果只包含了唯一一个抽象方法,那就是一函数式接口,就可以用Lamda表达式来表示。例如:
package com.tang.dem02;
/**
* @author tanglei
* @create 2021-05-09 10:31
*/
public class LamdaTest {
// //3.静态内部类
// static class Like2 implements ILike{
// @Override
// public void lambda() {
// System.out.println("I lIKE lambda2");
// }
// }
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
// Like2 like2 = new Like2();
// like2.lambda();
// //4.局部内部类
// class Like3 implements ILike{
// @Override
// public void lambda() {
// System.out.println("I lIKE lambda3");
// }
// }
// like = new Like3();//Like3 like3 = Like3();
// like.lambda();//like3.lambda();
// //5.匿名内部类,没有类的名称,必须借助接口或者父类
// like = new ILike() {
// @Override
// public void lambda() {
// System.out.println("I lIKE lambda4");
// }
// };//这相当于一行语句,需要加分号
// like.lambda();
//6.用lambda简化
like = ()->{
System.out.println("I lIKE lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface ILike{
void lambda();
}
//2.实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("I lIKE lambda");
}
}
}
7. 线程方法
1.线程停止 stop:
不推荐使用stop(),destory()方法,推荐自定义标志位来停止。
2.线程休眠 sleep:
每个线程都有一把锁,sleep不会释放锁。
3.线程礼让 yield:
让当前正在执行的线程暂停,但不阻塞,重新让CPU调度,让线程从运行状态转为就绪状态,礼让不一定成功。
package com.tang.dem02;
/**
* @author tanglei
* @create 2021-05-09 14:18
*/
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+ "线程停止执行");
}
}
结果:正常来说应该输出a线程执行开始到停止才到b线程执行,但是礼让之后就会可能让a,b都开始执行再停止执行。
4.线程合并 join:
待此线程执行完成后,再执行其他线程,其他线程阻塞,相当于插队。
package com.tang.dem02;
/**
* @author tanglei
* @create 2021-05-09 14:27
*/
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程vip来了");
}
}
public static void main(String[] args) throws InterruptedException {
//启动自己的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i < 100; i++) {
if (i == 20) {
thread.join();
}
System.out.println("main" + i);
}
}
}
结果:
8. 线程状态
package com.tang.dem02;
/**
* @author tanglei
* @create 2021-05-09 14:42
*/
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("--------");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);// NEW
//观察启动后
thread.start();//启动线程
state = thread.getState();
System.out.println(state);//Run
while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
Thread.sleep(100);
state = thread.getState();//更新状态
System.out.println(state);
}
}
}
结果:
…
9. 守护线程 daemon
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕:如man();
- 虚拟机不用等待守护线程执行完毕:如gc();
10. 线程同步
synchorized关键字可以加锁,保持同步,没有竞争才算线程安全;
调用对象上的同步实例方法需要给对象加锁,调用一个类上的同步静态方法需要给类加锁。
死锁:某一个代码块同时拥有两个以上对象的锁,就会发生死锁。
package com.tang.dem02;
/**
* @author tanglei
* @create 2021-05-09 15:28
*/
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑凉");
Makeup g2 = new Makeup(1,"白雪公主");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread {
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlname;
Makeup(int choice, String girlname) {
this.choice = choice;
this.girlname = girlname;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {//获得口红的锁
System.out.println(this.girlname + "获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror) {
System.out.println(this.girlname + "获得镜子的锁");
}
} else {
synchronized (mirror) {//获得口红的锁
System.out.println(this.girlname + "获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick) {
System.out.println(this.girlname + "获得口红的锁");
}
}
}
}
Lock
- lock():得到一个锁,unlock释放锁,encondition()返回一个绑定到改lock实例的condition对象
- ReentrantLock是lock的一个具体实现,该方法用于创建相互排斥的锁,可以创建具有特定的公平策略的锁。如果公平策略为真,则确保等待时间最长的线程首先获得该锁
ReentrantLock lock = new ReentranLock();
try{
lock.lock;//加锁
}catch{
...
}
finally{
lock.unlock//解锁
}
11. 线程协作
通过创建lock对象的newCondition()方法而创建的对象。
await()让当前线程进入等待,直到条件发生,sinal()方法唤醒一个等待的线程,而signalAll()唤醒所有等待的线程。
生产者和消费者问题。
package com.tang.dem02;
/**
* @author tanglei
* @create 2021-05-09 15:55
* //利用缓冲区解决:管程法
*/
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了" + i + "只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了" + container.pop().id + "只鸡");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int count =0;
//生产者生产产品
public synchronized void push(Chicken chicken){
//如果容器满了,就需要等待消费者消费
if(count == chickens.length){
//通知消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果容器没有满那么就需要生产产品
chickens[count]=chicken;
count++;
this.notify();
}
//消费者消费产品
public synchronized Chicken pop(){
if(count==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
this.notifyAll();
return chicken;
}
}
12. 线程池
package com.tang.dem02;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author tanglei
* @create 2021-05-09 16:21
*/
public class TestPool {
public static void main(String[] args) {
//1.创建线程池
//newFixedThreadPool 参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}