Java多线程入门

线程学习

1、run()和start()

在这里插入图片描述

2、进程和线程的关系

在这里插入图片描述

程序是指令和数据的集合,是一个静态的概念

进程就是跑起来的程序,进程是系统分配资源的单位。一个进程中至少有一个线程

线程是cpu调度和指定的最小单位

3、Thread和Runnable

1、Thread创建线程

  • 继承Thread类

  • 重写run()方法

  • new 一个线程对象,调用start()方法启动线程

  • public class ThreadStep1 extends Thread{
        //创建Thread类
        @Override
        public void run() {
            System.out.println("this is a run test");
        }
        public static void main(String[] args) {
            new ThreadStep1().start();
            System.out.println("this is main sout");
        }
    }
    

2、Runnable接口

  • 实现Runnable接口

  • 重写run()方法

  • 将线程丢入Thread初始化

  • 使用Thread.strat()启动

    public class RunnableStep2 implements Runnable{
        @Override
        public void run() {
            System.out.println("runnable 方法");
        }
        public static void main(String[] args) {
            new Thread(new RunnableStep2()).start();
            System.out.println("this is main");
        }
    }
    

3、抢票例子

线程不安全

public class ThreadStep4 implements Runnable{

    //票数
    private int ticketNum = 10;


    @Override
    public void run() {
        while(true){
            if (ticketNum<=0)
                break;
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNum--+"票");
        }
    }

    public static void main(String[] args) {
        ThreadStep4 step4 = new ThreadStep4();

        new Thread(step4,"a").start();
        new Thread(step4,"b").start();
        new Thread(step4,"c").start();
    }
}
a-->拿到了第10票
c-->拿到了第8票
b-->拿到了第9票
b-->拿到了第6票
a-->拿到了第5票
c-->拿到了第7票
b-->拿到了第4票
c-->拿到了第3票
a-->拿到了第4票
b-->拿到了第2票
a-->拿到了第1票
c-->拿到了第0票

4、Callable接口

E:\Idea_workspace\多线程\learnDemo1\src\main\java\demo02\CallableStep5.java

  • 实现Callable接口,需要返回值接口
  • 重写call方法,需要抛出异常

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0lTtS0KY-1656512928187)(C:\Users\22142\AppData\Roaming\Typora\typora-user-images\image-20220621160723229.png)]

5、静态代理模式

真实对象和代理对象实现同一个接口

对于线程中来说,

Marry就是Thread类

HappyMarry就是run方法

package demo03_proxy;

public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany company = new WeddingCompany(new You());
        company.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("结婚前");
    }
}

4、Lambda表达式

E:\Idea_workspace\多线程\learnDemo1\src\main\java\demo04_lambda

适合用于匿名类

相当于实现类接口中唯一的方法

Runnable接口只有一个run方法,因此lambda可以用于Runnable接口的实现

 (params)->expression[表达式]
 (params)->statement [语句]
 (params)->{statements}

函数式接口

  • 任何一个接口,如果只包含唯一一个抽象方法,那他就是一个函数式接口
public interface Runable{
 	public abstract void run(){};
}
  • 对于函数式接口,可以通过lambda表达式来创建该接口的对象

演变

实现类->内部类->局部内部类->匿名类->lambda表达式

package demo04_lambda;

public class LambdaStep5 {

    //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();

        like = new Like2();
        like.lambda();

        //4、局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }
        like = new Like3();
        like.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 lambda1");
    }
}

使用

package demo04_lambda;

public class LambdaTest {

    public static void main(String[] args) {
        ILove love;
        //1.
        love = (String name)->{
            System.out.println("i love "+name);
        };
        love.love("1");
        //2.去掉参数类型
        love = (name)->{
          System.out.println("i love "+name);
        };
        love.love("2");
        //3、去掉括号
        love = name -> System.out.println("i love "+name);
        love.love("钝角");
    }
}
interface ILove{
    void  love(String name);
}

Lambda和Runnable

package demo04_lambda;

public class LambdaRunnable {

    public static void main(String[] args) {
        Runnable runnable = ()->{
            System.out.println("this is a runnable+lambda test");
        };
        new Thread(runnable).start();
    }
}

5、线程方法

1、线程五个状态

在这里插入图片描述

2、线程方法

3、停止线程

不建议使用stop 或destroy

  • 程序正常停止----设置执行次数
  • 使用标志位------设置flag标志

在run方法中使用while(flag)包含函数体,使得线程的停止可以由flag操纵,

创建一个方法控制线程的停止

public void run() {
    int i = 0;
    while(flag){
        System.out.println("run.....Thread"+i++);
    }
}
public void stop(){
    this.flag=false;
}

4 线程休眠

sleep(time) time的单位是毫秒

sleep 存在异常 InterruptedException

sleep 时间到达后线程进入就绪状态

sleep 可以模拟网络延时,倒计时

每一个对象都有一个锁, sleep不会释放锁

5 线程礼让 Yield

Thread.yield()

停止当前线程,转为就绪状态,重新由cpu选择运行

6 线程 join

停止其他线程,先执行完此线程

public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("join "+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin join = new TestJoin();
        Thread thread = new Thread(join);
        thread.start();

        for (int i = 0; i < 500; i++) {
            if (i == 200){
                thread.join();
            }
            System.out.println("main "+i);
        }
    }
}

在这里插入图片描述

在thread.join()之前,main和thread同时执行,之后在thread执行完后,继续执行main

7 线程状态 Thread.state

  • public static enum Thread.State
    extends Enum<Thread.State>
    

    线程状态。线程可以处于以下状态之一:

    • NEW
      尚未启动的线程处于此状态。
    • RUNNABLE
      在Java虚拟机中执行的线程处于此状态。
    • BLOCKED
      被阻塞等待监视器锁定的线程处于此状态。
    • WAITING
      正在等待另一个线程执行特定动作的线程处于此状态。
    • TIMED_WAITING
      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
    • TERMINATED
      已退出的线程处于此状态。

    一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

Thread.State state = thread.getState();

通过getState()获得线程当前状态

8 线程优先级 Priority

先设置优先级,然后start()

设置的只是优先调动的概率,不能一定按这个顺序

  • public static final intMAX_PRIORITY10
    public static final intMIN_PRIORITY1
    public static final intNORM_PRIORITY5

获取和改变优先级,范围1~10

getPriority()

setPriority(int x)

9 线程守护 daemon

setDaemon(boolean on)

默认是false,表示用户线程

true表示守护线程,在所有用户线程结束后自动结束

如下,you线程执行完毕后,god线程也会结束,不会一直执行

package demo09_ThreadDaemon;

public class ThreadDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认false-用户线程
        //守护线程和程序同一时间结束
        thread.start();

        new Thread(you).start();

    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("happy live");
        }
        System.out.println("hello god!");
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("god bless you");
        }
    }
}

6、线程同步

1、并发

并发:同一个对象被多个线程同时操作

多个线程访问时,用到线程池

2、队列和锁

队列和锁保证线程安全

synchronized

会损失性能

3、同步

  • 同步方法

    public synchronized void method(int args){}

    默认锁this

  • 同步块

    synchronized(obj){}

其中obj是需要锁的对象

synchronized (account){
    if (account.money-drawingMoney<0){
        System.out.println(Thread.currentThread().getName()+" 余额不足");
    }
    else {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money = account.money - drawingMoney;
        money = money + drawingMoney;
        System.out.println(account.name+" 余额为: "+ account.money);
        System.out.println(this.getName()+" 手中钱:"+ money);
    }
}

4、死锁

多个对象互相持有对方需要的资源

造成死锁

获得一个资源后不释放,再想获得另一个资源

死锁例子(嵌套synchronized)

synchronized (lipstick){
    System.out.println(this.girlName+" sync of lipstick");
    Thread.sleep(1000);
    synchronized (mirror){
    	System.out.println(this.girlName+" sync of mirror");
	}
}

5、Lock锁

Lock是显示锁 需要手动开关,synchronized是隐式锁,处理作用域自动释放

//定义可重复Lock锁
private final ReentrantLock lock = new ReentrantLock();
class TestLock implements Runnable{
    int tickeNum = 1000;
    //定义Lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            lock.lock();//加锁
            try {
                if (tickeNum>=0){
                    System.out.println("ticke: "+tickeNum--);
                } else {
                    break;
                }
            }finally {
                lock.unlock();
            }
        }
    }
}

7、线程通信

方法说明
wait()表示线程一直等待,直到接到通知(会释放锁)
wait(long timeout)指定线程等待的毫秒数
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上的所有处于等待状态的线程

上述方法只能在同步块中使用

1、管程法

生产者消费者问题

package demo10_sync;

//管程法:线程通信

//生产者、消费者、产品、缓存区
public class ProducerConsumer {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        new Producer(container).start();
        new Consumer(container).start();
    }
}

class Producer extends Thread{
    SynContainer synContainer;

    public Producer(SynContainer synContainer){
        this.synContainer = synContainer;
    }

    //生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了第:"+i+"鸡");
            synContainer.push(new Product(i));
            System.out.println("当前余量:"+(10-synContainer.count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Consumer extends Thread{
    SynContainer synContainer;

    public Consumer(SynContainer synContainer){
        this.synContainer = synContainer;
    }

    //消费

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了-->:"+synContainer.pop().id+"鸡");
            System.out.println("当前余量:"+(10-synContainer.count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Product{
    int id;
    public Product(int i){
        this.id = i;
    }
}

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、信号灯法 标志位

设置标志位,通过判断标志位进行等待或其他

package demo10_sync;

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new TVShow(tv).start();
        new Audience(tv).start();
    }
}
//节目
class TVShow extends Thread{
    TV tv;

    public TVShow(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if(i%2==0){
                tv.player("快本");
                //模拟播放
            }
            else {
                tv.player("天天向上");
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//观众
class Audience extends Thread{
    TV tv;

    public Audience(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("看完了-->"+tv.showing);
            tv.watch();
        }
    }
}
//电视机
class TV{
    //两种状态:看节目 节目,观众看;广告,观众等
    boolean flag = true;
    //电视正在播放的
    String showing;

    public synchronized void player(String show){
        if (!flag){
            try {
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        this.showing = show;
        this.flag = !this.flag;
        System.out.println("正在播放:"+show+"...");
        //节目切换,通知观看
        this.notifyAll();
    }
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }

        //观众看完
        this.flag = !this.flag;
        //通知换节目
        this.notifyAll();
    }
}

3、线程池

1、新建一个线程池

ExecutorService service = Executors.newFixedThreadPool(10);

public static ExecutorService newFixedThreadPool(int nThreads)

创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。 在任何时候,最多nThreads线程将处于主动处理任务。 如果所有线程处于活动状态时都会提交其他任务,则它们将等待队列中直到线程可用。 如果任何线程由于在关闭之前的执行期间发生故障而终止,则如果需要执行后续任务,则新线程将占用它。 池中的线程将存在,直到它明确地为shutdown

  • 参数

    nThreads - 池中的线程数

  • 结果

    新创建的线程池

  • 异常

    IllegalArgumentException - 如果是 nThreads <= 0

2、执行
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
3、 关闭
service.shutdown();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值