Java中的多线程

1. 线程实现

1.1 线程创建(三种方法)

  1. Java下载图片的方法:通过FileUtils.copyURLToFile方法
  2. 获取线程名字Thread.currentThread.getName()
class WebDownloader {
    //下载方法
    public void downloader(String url, String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

继承Thread类

继承后实现run方法,在main方法中可以new新对象直接调用start方法。

实现Runnable接口 (最重要)

实现run方法后,在main方法中new了新对象,还要将其传入new Thread中,才可以调用start方法。
还可以在newThread传入对象的同时,传入一个字符串表示线程名字。

实现Callable接口

实现Callable接口<Boolean>有返回值,创建目标对象,创建执行服务,提交执行代替.start,获取结果bool,关闭服务。

类 c = new C();

  1. 创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(1);
  2. 提交执行 Future<Boolean> r=ser.submit(c)
  3. 获取结果 boolean res= r.get();
  4. 关闭服务 ser.shutdownNow()

1.2 静态代理

真实对象和代理对象都实现同一个接口。
代理对象做真实对象做不了的事,让真实对象专注做自己的事。

// 线程中使用的就是代理模式
public class Demo8_StaticProxy {
    public static void main(String[] args) {
        new Thread(()-> System.out.println("我爱你")).start();
        new WeddingCompany(new You()).happyMarry();
    }
}

1.3 Lamda表达式

对于实现函数式接口的类可以被Lamda表达式替代。
函数式接口定义

包含唯一一个抽象方法。
new 接口名({函数方法}); 等价于
(int a,int b) ->{
sout();
}

  1. 接口实现只有一行代码时可以省略{}
  2. 可以省略参数类型 ,要省略就必须全省略
  3. 多个参数必须有()

2.线程的状态

2.1 状态转换

new -》创建状态
.start -》就绪状态
.sleep /wait -》阻塞状态

2.2 线程方法

方法 说明
setPriority(int newPriority) 更改线程优先级
static void sleep(long millis) 指定毫秒休眠
void join 等待该线程终止
static void yield() 暂停当前 正在执行的线程对象,并执行其他线程
void interrupt() 中断线程,不推荐~!!
boolean isAlive() 检测是否存活

停止线程 自己写stop

不推荐stop/destroy等JDK不建议的方法。
不建议使用死循环
建议正常停止,使用标志位。

image

线程休眠 Thread.sleep(int mills)

  1. sleep时间到达后进入就绪状态
  2. 可以模拟网络延时,倒计时
  3. 每一个对象都有一把锁,Sleep不会释放锁
//模拟时钟
public class Recount implements Runnable{
    @Override
    public void run() {
        Date date = new Date(System.currentTimeMillis());
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(new SimpleDateFormat("YY:MM:dd:HH:mm:ss").format(date));
            date=new Date(System.currentTimeMillis());
        }
    }

    public static void main(String[] args) {
        Recount recount = new Recount();
        new Thread(recount).start();
    }
}

线程礼让 Thread.yield()

  1. 让当前线程暂停,回到就绪状态。
  2. CPU重新调度,所以礼让不一定成功。

线程插队 对象thread.join()

  1. 其他线程阻塞等待对象线程执行完毕
  2. 由其他线程 使用对象线程引用.join()。相当于某线程让别的线程先强制执行。
public class Jointest implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<500;i++) {
            System.out.println("vip线程来啦"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Jointest jointest = new Jointest();
        Thread vip =new Thread(jointest);
        vip.start();
        for (int i = 0; i < 200; i++) {
            if(i==100) {
                vip.join();
            }
            System.out.println("我是主线程"+i);
        }
    }
}

观察线程状态 Thread.State

  1. NEW:刚new出来的线程
  2. RUNNABLE:执行了statrt,在虚拟机中执行的线程
  3. BLOCKED:被阻塞的线程
  4. WAITING:正在等待另一个线程执行特定动作的
  5. TIMED_WAITING:正在等待到指定时间的线程。
  6. TERMINATED:已推出的线程。
    Thread.State state = thread.getState()

线程优先级 thread.setPriority(int x)

  1. 默认优先级是5
  2. 范围1-10
  3. 常量 Thread.Max_PRIORITY /MIN_PRIORITY

守护线程 thread.setDaemon(boolean )

  1. 线程分为 用户线程守护线程
  2. 虚拟机确保用户线程执行完毕,不用等待守护线程执行完毕
  3. 常见的守护线程有 后台记录操作日志,监控内存,垃圾回收
  4. 设置用户线程为守护线程: thread.setDaemon(true)

3. 线程同步

多线程对同一对象访问,需要线程同步。这是一种等待机制,多个需要访问该对象的线程进入对象的等待池形成队列。
同步的形成条件: 队列+锁

3.1 不安全的情况

由于线程在自己内存处理导致

  1. 购票
  2. 取钱
  3. 集合 (多线程操作同一块。)

3.2 隐式锁 Synchronized

同步方法

在方法中加入关键字,相当于锁this这个对象本身。方法一旦执行,就独占锁直到方法返回,后面被阻塞的线程才能获得锁。

同步代码块 Synchronized(OBJ) {}

OBJ称为同步监视器,将变量/共享资源作为同步监视器可以达到同步的目的。
在原有代码上外套{}即可

JUC安全集合类型扩充

java.util.concurrent包中的
CopyOnWriteArrayList集合是安全的。底层有votilaite关键字。

3.3 显示锁 Lock

  1. concurrent.locks.Lock是一个接口,线程开始访问共享资源应先获取Lock对象。
  2. Lock的实现类 可重入锁ReentrantLock,可以显式加锁解锁。
    image

4. 线程通信

4.1 通信方法

都是Object类的方法,只能在同步方法或同步代码块中使用,否则会抛出异常。

方法名 作用
wait() 线程释放锁并等待
wait(long timeout) 指定等待的毫秒数
notify() 唤醒一个处于等待状态的线程
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程。

生产者消费者举例

  1. 容器方法
    消费者和生产者各自定义缓冲区,调用容器的方法;缓冲区定义容器大小,定义容器增减方法。
  2. 信号灯法
    消费者和生产者各自定义传递的数据类,调用容器方法。缓冲区通过自定义的flag来控制。
public class Demo33_ThreadPC {
    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++) {
            container.push(new Product(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 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) {
        //如果容器满了,需要等待消费者消费
        /*如果是if的话,假如消费者1消费了最后一个,这是index变成0此时释放锁被消费者2拿到而不是生产者拿到,这时消费者的wait是在if里所以它就直接去消费index-1下标越界,如果是while就会再去判断一下index得值是不是变成0了*/
        while (count == products.length) {
            //通知消费者消费,等待生产
            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;
    }
}

posted @ 2023-07-07 12:14  不準  阅读(24)  评论(0)    收藏  举报