绊夏微凉

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

线程学习

参考链接:

https://www.zhihu.com/question/264627396
https://blog.csdn.net/weixin_43249530/article/details/88064361
https://blog.csdn.net/rjkkaikai/article/details/81016445
https://blog.csdn.net/m0_45067620/article/details/108026299
https://www.cnblogs.com/dolphin0520/p/3920385.html

1、线程和进程

进程

进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。
在Java中,当启动main方法时其实就是启动了一个JVM的进程,而main方法所在的线程就是这个进程的一个线程,也称主线程。

线程

线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进行不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和方法栈,所以系统在产生一个线程,或者在各个线程之间作切換工作时,负担要比进程小得多,也正因为如此,线程也被成为轻量级进程。

2、线程的创建

package com.example.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Demo01 {

    public static void main(String[] args) {
        // 1、继承Thread创建线程
        MyThread myThread = new MyThread();
        myThread.start();
        // 2、实现Runnable接口创建线程
        new Thread(new MyThread_1()).start();
        // 3、实现Callable接口创建线程
        FutureTask<String> futureTask = new FutureTask(new MyThread_2());
        new Thread(futureTask).start();
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }

}

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承Thread创建线程");
    }
}

class MyThread_1 implements Runnable {

    @Override
    public void run() {
        System.out.println("实现Runnable接口");
    }
}

class MyThread_2 implements Callable<String> {

    @Override
    public String call() throws Exception {
        return "实现Callable接口";
    }
}

3、线程的方法

sleep方法

sleep为线程休眠方法,单位为毫秒,休眠时由运行态到阻塞态,休眠结束后由阻塞态到就绪态
线程休眠:指的是让线程暂缓执行一下,等到了预计时间之后再恢复执行。线程休眠会立马交出CPU,让CPU去执行其他的任务。注意:sleep方法不会释放锁,意思是如果当前线程持有对某个对象的锁,即使调用sleep方法,其他线程也无法访问这个对象。

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        new Thread(myThread_3, "小明").start();
        new Thread(myThread_3, "小红").start();
    }

}

class MyThread_3 implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "延时前");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "延时后");
    }
}

结果

yield方法

线程礼让方法,由运行态到就绪态,停止一下后再由就绪态到运行态,不会释放锁
yield让当前运行线程回到就绪态,允许具有相同优先级的其他线程获得运行机会。目的是让相同优先级的线程之间能适当的轮转执行,但实际中无法保证yield达到礼让目的,因为礼让的线程还有可能被线程调度程序再次选中。

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        new Thread(myThread_3, "小明").start();
        new Thread(myThread_3, "小红").start();
        new Thread(myThread_3, "小方").start();
    }

}

class MyThread_3 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            Thread.yield();
            System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
        }
    }
}

结果:

join方法
让“主线程”等待“子线程”结束之后才能继续运行,会释放锁
没加join的方法

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        Thread thread = new Thread(myThread_3, "小明");
        thread.start();
        System.out.println("当前线程:" + Thread.currentThread().getName());
    }

}

class MyThread_3 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
        }
    }
}

结果

加join的方法

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        Thread thread = new Thread(myThread_3, "小明");
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("当前线程:" + Thread.currentThread().getName());
    }

}

class MyThread_3 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
        }
    }
}

结果

获取状态方法

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        Thread thread = new Thread(myThread_3, "小明");
        // 获取线程状态
        System.out.println(thread.getState());
        thread.start();
        // 获取线程状态
        System.out.println(thread.getState());
    }

}

class MyThread_3 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
        }
    }
}

结果

设置优先级

优先级从高到低:10到1 优先级越高,被优先执行的可能性越高,但是只是可能。 设置优先级,要在调用线程前设置,如果设置优先的范围超过1到10会报错
package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        Thread thread = new Thread(myThread_3, "小明");
        Thread thread_1 = new Thread(myThread_3, "小红");
        Thread thread_2 = new Thread(myThread_3, "小方");
        thread_2.setPriority(Thread.MAX_PRIORITY);
        thread_1.setPriority(Thread.MIN_PRIORITY);
        thread.start();
        thread_1.start();
        thread_2.start();
    }

}

class MyThread_3 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println("当前线程:" + Thread.currentThread().getName() + ", i=" + i);
        }
    }
}

结果

4、线程的同步

解决多个线程访问同一个资源,出现的问题

问题

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        new Thread(myThread_3, "小明").start();
        new Thread(myThread_3, "小红").start();
        new Thread(myThread_3, "小方").start();
    }

}

class MyThread_3 implements Runnable {

    private int ticket = 10;

    @Override
    public void run() {
        while (true) {
            if (ticket <= 0) {
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
        }
    }
}

方法一:使用synchronized代码块

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        new Thread(myThread_3, "小明").start();
        new Thread(myThread_3, "小红").start();
        new Thread(myThread_3, "小方").start();
    }

}

class MyThread_3 implements Runnable {

    private int ticket = 10;

    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (ticket <= 0) {
                    break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
            }
        }
    }
}

方法二:使用synchronized方法

package com.example.thread;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        new Thread(myThread_3, "小明").start();
        new Thread(myThread_3, "小红").start();
        new Thread(myThread_3, "小方").start();
    }

}

class MyThread_3 implements Runnable {

    private int ticket = 10;
    private boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }
    }

    public synchronized void buy() {
        if (ticket <= 0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
    }
}

方法三:使用锁

package com.example.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo02 {

    public static void main(String[] args) {
        MyThread_3 myThread_3 = new MyThread_3();
        new Thread(myThread_3, "小明").start();
        new Thread(myThread_3, "小红").start();
        new Thread(myThread_3, "小方").start();
    }

}

class MyThread_3 implements Runnable {

    private int ticket = 10;
    private static Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (ticket <= 0) {
                    break;
                }
                Thread.sleep(100);
                System.out.println("当前线程" + Thread.currentThread().getName() + ticket--);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

5、线程间的通信

wait()、notify()、和notifyAll()

wait()、notify()和notifyAll()是Object类中的方法
wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写,调用方法时,当前线程必须拥有对象的monitor(即锁),所以必须在同步块或者同步方法中进行。
调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor,调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但并不释放锁)。
调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程。当有多个线程都在等待该对象的monitor时,只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程。
notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。

生产者和消费者问题

方法一:管程法(使用容器)

生产者生产了10个商品,等待消费者消费后,才能生产,消费者消费了10个商品,等待生产者生产后在能消费

package com.example.thread;

public class Demo03 {
    public static void main(String[] args) {
        // 设置商店
        Shop shop = new Shop();
        // 设置消费者
        new Consumer(shop).start();
        // 设置生产者
        new Provider(shop).start();
    }
}

// 定义商品
class Chicken {
    // 定义商品的id
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}

// 定义商店
class Shop {
    // 定义容器
    Chicken[] chickens = new Chicken[10];
    // 商品的个数
    int count = 0;
    // 生产商品
    public synchronized void push(Chicken chicken) {
        // 如果生产了10只商品,等待消费者消费
        if (count == chickens.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 把生产的商品放入容器
        chickens[count] = chicken;
        // 商品个数+1
        count++;
        System.out.println("生产了第" + chicken.id + "个商品");
        // 通知消费者已生产
        this.notifyAll();
    }

    // 消费商品
    public synchronized void pop() {
        // 如果商品个数为0,等待生产者生产
        if (count == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 数组是从0开始
        count--;
        // 获取商品
        Chicken chicken = chickens[count];
        System.out.println("消费了第" + chicken.id + "个商品");
        // 通知生产已消费
        this.notifyAll();
    }
}

// 定义生产者
class Provider extends Thread {
    private Shop shop;

    public Provider(Shop shop) {
        this.shop = shop;
    }

    @Override
    public void run() {
        // 生产商品
        for (int i = 1; i <= 20; i++) {
            shop.push(new Chicken(i));
        }
    }
}

// 定义消费者
class Consumer extends Thread {
    private Shop shop;

    public Consumer(Shop shop) {
        this.shop = shop;
    }

    @Override
    public void run() {
        // 消费商品
        for (int i = 0; i < 11; i++) {
            shop.pop();
        }
    }
}

方法二:信号灯(使用标志位)

生产者生产时,消费者等待,消费者消费时,生产者等待

package com.example.thread;

public class Demo03 {
    public static void main(String[] args) {
        // 设置商店
        Shop shop = new Shop();
        // 设置消费者
        new Consumer(shop).start();
        // 设置生产者
        new Provider(shop).start();
    }
}

// 定义商店
class Shop {
    // 信号灯
    boolean flag = true;
    // 产品名称
    String goodsName;
    // 生产商品
    public synchronized void push(String goodsName) {
        // 消费者消费时等待
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("生产商品的名称:" + goodsName);
        // 设置商品名称
        this.goodsName = goodsName;
        // 生产完等待消费者消费
        flag = !flag;
        this.notifyAll();
    }

    // 消费商品
    public synchronized void pop() {
        // 生产者生产时等待
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("消费商品的名称:" + goodsName);
        // 消费者等待生产者生产
        flag = !flag;
        this.notifyAll();
    }
}

// 定义生产者
class Provider extends Thread {
    private Shop shop;

    public Provider(Shop shop) {
        this.shop = shop;
    }

    @Override
    public void run() {
        // 生产商品
        for (int i = 0; i < 15; i++) {
            if (i % 2 == 0) {
                shop.push("烤鸡");
            } else {
                shop.push("汉堡");
            }
        }
    }
}

// 定义消费者
class Consumer extends Thread {
    private Shop shop;

    public Consumer(Shop shop) {
        this.shop = shop;
    }

    @Override
    public void run() {
        // 消费商品
        for (int i = 0; i < 15; i++) {
            shop.pop();
        }
    }
}

posted on 2021-08-19 11:34  绊夏微凉  阅读(41)  评论(0)    收藏  举报