基本概念
-
程序
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
-
进程
进程是执行程序的一次执行过程,它是一个动态的概念,是系统资源分配的单位
-
线程
通常在一个进程中可以包含若干个线程,一个进程中至少有一个线程。线程是CPU调度和执行的单位
多线程实现
继承Thread类 extends Thread
- 继承Thread类,重写run()方法,调用start开启线程
- 注意:线程开启不一定立即执行,由CPU调度执行
- 不建议使用:OOP单继承局限性
public class TestThread01 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 100; i++) {
System.out.println("run--"+i);
}
}
public static void main(String[] args) {
//mian线程,主线程
//创建一个线程对象
TestThread01 testThread01 = new TestThread01();
//调用start方法开启线程
testThread01.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main--"+i);
}
}
}
实现runnable接口 implements Runnable
- 创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
- 推荐使用:避免单继承局限性,灵活方便,同一个对象能被多个线程使用
public class TestThread03 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("run---"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread03 testThread03 = new TestThread03();
//创建线程对象,通过线程对象来开启线程,代理
new Thread(testThread03).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main---"+i);
}
}
}
实现callable接口 implements Callable<>
- 可以自定义返回值
- 可以抛出异常
public class TestCallable implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("-->"+i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable testCallable = new TestCallable();
//创建执行服务
ExecutorService ser= Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> r1=ser.submit(testCallable);
//获取结果
boolean rs = r1.get();
//关闭服务
ser.shutdown();
}
}
线程池
public class TestPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);//参数为池子大小
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName()+i);
}
}
}
静态代理
- 真实对象和代理对象要实现同一个接口
- 代理对象要代理真实角色
- 有了代理对象,很多事就不用真实对象自己做
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.Marry();
}
}
interface Marry{
void Marry();
}
//真实角色,主人公
class You implements Marry{
@Override
public void Marry() {
System.out.println("结婚");
}
}
//代理角色,帮助作用
class WeddingCompany implements Marry{
//代理真实目标角色
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void Marry() {
before();
this.target.Marry();
after();
}
public void before(){
System.out.println("结婚前,布置现场");
}
public void after(){
System.out.println("结婚后,收尾款");
}
}
Lamda表达式
-
jdk8新特性
-
为什么要使用lamda表达式
- 避免匿名内部类定义过多
- 让代码看起来更简洁
- 去掉了一堆无意义的代码,只留下核心逻辑
推导lamda表达式过程
public class TestLamda01 {
//3.静态内部类
static class Like2 implements ILike{
@Override
public void lamda() {
System.out.println("I like lamda2");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lamda();
like=new Like2();
like.lamda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void lamda() {
System.out.println("I like lamda3");
}
}
like=new Like3();
like.lamda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like=new ILike() {
@Override
public void lamda() {
System.out.println("I like lamda4");
}
};
like.lamda();
//6.用lamda简化
like=()->{System.out.println("I like lamda5");};
like.lamda();
}
}
//1.定义一个函数式接口
interface ILike{void lamda(); }
//2.实现类
class Like implements ILike{
@Override
public void lamda() {
System.out.println("I like lamda1");
}
}
更加简化
public class TestLamda02 {
public static void main(String[] args) {
ILove love=(int a)->{
System.out.println("I love you"+a);
};
//简化1 去掉参数类型
love=(a)->{
System.out.println("I love you"+a);
};
//简化2 去掉括号
//注意:若有多个参数,不能去掉括号
love=a->{
System.out.println("I love you"+a);
};
//简化3 去掉花括号
//注意:lamda表达式只能有一行代码的情况下才能简化为一行,如果有多行代码,那么应用代码块包裹
love=a->System.out.println("I love you"+a);
love.love(520);
}
}
interface ILove{ void love(int a);}
函数式接口 Functional Interface
- 定义
-
任何接口,如果只包含唯一一个抽象方法,那它就是一个函数式接口
public interface exam{ public abstract void run(); } -
对于函数式接口,我们可以通过lamda表达式来创建该接口的对象
线程 thread
线程状态 state
- 新生状态:Thread t=new Thread() 线程对象一旦创建就进入到了新生状态
- 就绪状态:当调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行
- 运行状态:进入运行状态,线程菜真正执行线程体的代码块
- 阻塞状态:当调用sleep,wait或同步锁定时,线程进入阻塞状态,代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待CPU调度执行
- 死亡状态:线程中断或结束,一旦进入死亡状态,就不能再次启动
线程停止 stop
- 建议线程正常停止—>利用次数,不建议死循环
- 建议使用标志位
- 不要使用stop或destroy等过时或JDK不建议使用的方法
public class TestStop implements Runnable{
//设置一个标志位
private boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("run...Thread"+i++);
}
}
//设置一个公开方法停止线程,转换标志位
public void stop(){this.flag=false;}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==900){
testStop.stop();//让线程停止
System.out.println("线程停止");
}
}
}
}
线程休眠 sleep
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后线程进入就绪状态
- 每一个对象都有一个锁,sleep不会释放锁
线程礼让 yield
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功!有可能执行礼让的线程又被CPU重新调度
线程强制执行 join
- join合并线程,待此线程执行完成再执行其他线程,其他线程阻塞
线程优先级 priority
- 优先级高的线程不一定先运行
- 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了
- 先设置优先级再启动线程
- 优先级范围为0~10,默认为5
守护线程 daemon
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
线程同步 synchronized
同步方法
-
public synchronized void method(int args){}
-
注意:若将一个大的方法申明为同步方法将会影响效率
public class UnsafeBuyying {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station,"张三").start();
new Thread(station,"李四").start();
new Thread(station,"王五").start();
}
}
class BuyTicket implements Runnable{
private int ticketNums = 10;
boolean flag = true;//外部停止方式
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
//synchronized 同步方法,锁的是this
public synchronized void buy() throws InterruptedException {
//判断是否有票
if(ticketNums<=0){
flag=false;
return;
}
//模拟延时
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--);
}
}
同步块
- synchronized(Obj){}
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"私房钱");
Drawing me =new Drawing(account,50,"我");
Drawing friend =new Drawing(account,100,"朋友");
me.start();
friend.start();
}
}
//账户
class Account{
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Account account;
int drawing;//取出的钱
int now=0;//现在手上的钱
@Override
//取钱
//synchronized 默认锁的是this.
public void run() {
//要锁的对象应是变化的量
synchronized (account){
//余额
account.money-=drawing;
//现在手上的钱
now+=drawing;
if(account.money-drawing<0){
System.out.println(Thread.currentThread().getName()+"没有足够的余额");
return;
}
//模拟延时,放大出错的概率
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(account.name+"余额为:"+account.money);
//因为继承了Thread类,在这里相当于Thread.currentThread().getName()
//子类里无getName()同名方法,可以用this
System.out.println(this.getName()+"现在手里的钱为:"+now);
}
}
public Drawing(Account account, int drawing, String name){
//Thread类中一个构造方法:传入一个字符串来改变线程的名字
super(name);//super()只能写在构造方法第一行
this.account=account;
this.drawing=drawing;
}
}
锁 Lock
-
Lock是显式锁;synchronized是隐式锁,出了作用域自动释放
-
Lock只有代码块锁,synchronized有代码块锁和方法锁
-
使用Lock锁,JVM将花费较少时间来调度线程,性能更好,并且有更好的拓展性
-
优先使用顺序:
Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
Buying buying = new Buying();
new Thread(buying).start();
new Thread(buying).start();
new Thread(buying).start();
}
}
class Buying implements Runnable{
int ticketNums = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if (ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(ticketNums--);
}else {
break;
}
}finally {
lock.unlock();//解锁
}
}
}
}
死锁 deadlock
-
产生死锁的四个必要条件
- 互斥条件
- 请求与保持条件
- 不剥夺条件
- 循环等待条件
-
例子:
//死锁:多个线程互相占据对方需要的资源,形成僵持 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 girl; public Makeup(int choice, String girl) { this.choice = choice; this.girl = girl; } @Override public void run() { makeup(); } //化妆,持有对方的锁 private void makeup(){ if (choice==0){ synchronized (lipStick){//获得口红的锁 System.out.println(this.girl+"获得口红的锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (mirror) {//想要获得镜子 System.out.println(this.girl + "获得镜子的锁"); } } // synchronized (mirror){//想要获得镜子 // System.out.println(this.girl+"获得镜子的锁"); // } }else { synchronized (mirror){ System.out.println(this.girl+"获得镜子的锁"); try { Thread.sleep(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } synchronized (lipStick){ System.out.println(this.girl+"获得口红的锁"); } } // synchronized (lipStick){ // System.out.println(this.girl+"获得口红的锁"); // } } } }
生产者消费者问题
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
- 在生产者消费者问题中,仅有synchronized是不够的
- synchronized可阻止并发更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之间的消息传递(通信)
管程法
- 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
- 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
- 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”,生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
//生产者消费者模型-->利用缓冲区解决:管程法
//生产者 消费者 产品 缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Producer(synContainer).start();
new Consumer(synContainer).start();
}
}
//生产者
class Producer extends Thread{
SynContainer container;
public Producer(SynContainer container){
this.container=container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产了"+i+"个产品");
container.push(new Product(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 Product{
int id;
public Product(int id){
this.id=id;
}
}
//缓冲区
class SynContainer{
//设置容器大小
Product[] products = new Product[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Product product){
//如果容器满了,需要等待消费者消费
while (count == 10) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,需要丢入产品
products[count] = product;
count++;
//可以通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Product pop(){
//判断能否消费
while (count == 0) {
try {
this.wait();//等待生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Product product = products[count];
//消费完了,通知生产者生产
this.notifyAll();
return product;
}
}
信号灯法
//生产者消费者问题2:信号灯法,设置标志位
public class TestPC02 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Viewer(tv).start();
}
}
//生产者-->演员
class Player extends Thread{
TV tv;
public Player(TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
this.tv.play("唱跳rap");
}else {
this.tv.play("篮球");
}
}
}
}
//消费者-->观众
class Viewer extends Thread{
TV tv;
public Viewer(TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.tv.watch();
}
}
}
//产品-->节目
class TV{
//演员表演,观众等待 T
//观众观看,演员等待 F
String name;//表演的节目
boolean flag = true;
//表演
public synchronized void play(String name){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("演员表演了:"+name);
//通知观众观看
this.notifyAll();
this.name=name;
this.flag=!flag;
}
//观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("观看了:"+name);
//通知演员表演
this.notifyAll();
this.flag=!flag;
}
}
本文详细解析了程序、进程、线程的基本概念,探讨了多线程实现的两种方式,包括继承Thread和实现Runnable接口,以及Callable接口的使用。随后介绍了线程池、静态代理和Lambda表达式的应用。涵盖了线程状态、控制方法如stop、sleep和yield,以及线程优先级和守护线程。文章还讨论了死锁、生产者消费者问题和同步解决方案,如管程法和信号灯法。
11万+

被折叠的 条评论
为什么被折叠?



