Java基础(01)——多线程

1、创建线程的三种方式

  • 继承Thread类
//线程实现方式一
public class ThreadDome01 extends Thread{
    
    

    @Override
    public void run() {
    
    
        for (int i=0;i<20;i++){
    
    
            System.out.println("看小说"+i);
        }
    }
    public static void main(String[] args) {
    
    
        //开启ThreadDome01线程
        new ThreadDome01().start();
        for (int i=0;i<2000;i++){
    
    
            System.out.println("主线程执行"+i);
        }
    }
}
  • 实现Runnable接口
public class RunnableDome01 implements Runnable{
    
    
    @Override
    public void run() {
    
    
        for(int i = 0;i<20;i++){
    
    
            System.out.println("runnable实现线程"+i);
        }
    }

    public static void main(String[] args) {
    
    
        //开启实现Runnable接口的线程
        RunnableDome01 dome01 = new RunnableDome01();
        new Thread(dome01).start();
        for (int i = 0;i<200;i++){
    
    
            System.out.println("main线程"+i);
        }
    }
}
  • 实现Callable接口
    例1:
public class CallableDome01 implements Callable<Boolean> {
    
    
    @Override
    public Boolean call(){
    
    
       for(int i = 0;i<20;i++){
    
    
            System.out.println("runnable实现线程"+i);
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CallableDome01 t1 = new CallableDome01();

        //1、开启服务
        ExecutorService executor = Executors.newFixedThreadPool(3); //3为开启线程个数

        //2、开启线程
        Future<Boolean> f1 = executor.submit(t1);
      
        //3、获取返回值
        Boolean aBo1 = f1.get();

        //4、关闭服务
        executor.shutdown();
    }
}

例2:

public class CallableDome02 {
    
    
    public static void main(String[] args) {
    
    
        FutureTask futureTask = new FutureTask(new MyThread());
        new Thread(futureTask).start();
        try {
    
    
            Integer integer = (Integer) futureTask.get();
            System.out.println(integer);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}
class MyThread implements Callable{
    
    
    @Override
    public Integer call() throws Exception {
    
    
        return 100;
    }
}

2、线程方法和状态

在这里插入图片描述

  • sleep() 线程休眠
    线程休眠,不会释放锁,会阻塞

  • yield() 礼让方法
    对于同一个对象,开启的两个线程,进入cup中的线程调用yield()方法,会改变线程为就绪状态, 下一次两个线程谁先执行看cup心情

  • setpriority() 设置线程的优先级
    优先级默认是5,权重大的优先执行

  • join() 线程插队
    可以使插队的线程优先主线程执行

  • setDaemon(true) 设置线程为守护线程
    Thread thread = new Thread(dome02);
    thread.setDaemon(true);

3、线程同步

3.1、线程同步中死锁问题

  • 什么是死锁:多个线程持有对方的资源,形成僵持
  • 产生死锁的四个条件:
    1、互斥条件:一个资源每次只能被一个线程访问
    2、请求与保持条件:一个进程因请求资源而阻塞式,对以获得的资源保持不放
    3、不剥夺条件:进程获得的资源,在未使用完成之前,不能被强行剥夺
    4、循环等待条件 :若干个进程之间形成一种头尾相接的循环等待资源关系
    线程死锁代码:
public class DeadLock {
    
    
    public static void main(String[] args) {
    
    
        new Thread(new TestThread(1)).start();
        new Thread(new TestThread(0)).start();
    }

}
class A{
    
    }
class B{
    
    }

class TestThread implements Runnable{
    
    
    static A a = new A();
    static B b = new B();
    private int flag;
    public TestThread(int flag){
    
    
        this.flag = flag;
    }
    @Override
    public void run() {
    
    
        if (flag == 1){
    
    
            synchronized (a){
    
                  System.out.println(Thread.currentThread().getName()+"获取的 a 对象的锁");
                try {
    
    
                    Thread.sleep(1000); //模拟延迟,不让1一下拿到两个对象的锁
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                //1、死锁
                /*synchronized (b){
                    System.out.println(Thread.currentThread().getName()+"获取的 b 对象的锁");
                }*/
            }
            //2、解决死锁的方法
            synchronized (b){
    
    
                System.out.println(Thread.currentThread().getName()+"获取的 b 对象的锁");
            }
        }else if (flag == 0){
    
    
            synchronized (b){
    
    
                System.out.println(Thread.currentThread().getName()+"获取的 b 对象的锁");
                try {
    
    
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                //1、死锁
                /*synchronized (a){
                    System.out.println(Thread.currentThread().getName()+"获取的 a 对象的锁");
                }*/
            }
            //2、解决死锁
            synchronized (a){
    
    
                System.out.println(Thread.currentThread().getName()+"获取的 a 对象的锁");
            }
        }
    }
}

3.2、实现线程同步

  • synchronized 同步代码块
    锁的是对象,一般是公共资源,可用于方法。
//使用同步代码块来解决Arraylist线程不安全问题
public class ListTest {
    
    
    public static void main(String[] args) {
    
    
        //arraylist是线程不安全的
        List<String> list = new ArrayList<>();

        synchronized (list){
    
    
            for (int i = 0; i < 1000; i++) {
    
    
                new Thread(()->{
    
    
                    list.add(Thread.currentThread().getName());                
                }).start();
            }
        }
        try {
    
    
            Thread.sleep(8000);
            System.out.println(Thread.currentThread().getName());
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
  • lock锁
    Reentrantlock 可重入所
public class LockDome {
    
    
    public static void main(String[] args) {
    
    
        Test01 test01 = new Test01();
        new Thread(test01).start();
        new Thread(test01).start();
        new Thread(test01).start();
    }
}

class Test01 implements Runnable{
    
    

    private int i = 10;
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
    
    
        while (true){
    
    
            try {
    
    
                lock.lock();
                if (i>0){
    
    
                    try {
    
    
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }                 System.out.println(Thread.currentThread().getName()+"拿到了第"+i--+"票");
                }else {
    
    
                    break;
                }
            }finally {
    
    
                lock.unlock();
            }
        }
    }
}

4、线程通信

  • 通过缓冲区(管程法)
/**
 * 测试生产者和消费者模型—> 利用缓冲区解决:管程法
 * 生产者、消费者、产品、缓存区
 */
public class TestPC {
    
    
    public static void main(String[] args) {
    
    
        SynContainer container = new SynContainer();
        new Thread(new Productor(container)).start();
        new Thread(new Consumer(container)).start();
    }
}

//生产者
class Productor implements Runnable{
    
    
    SynContainer container;
    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 implements Runnable{
    
    
    SynContainer container;
    Consumer(SynContainer container){
    
    
        this.container = container;
    }
    @Override
    public void run() {
    
    
        for (int i = 0; i < 100; i++) {
    
    
            container.pop(new Chicken(i));
            System.out.println("消费了第"+i+"只鸡");
        }
    }
}

//产品
class Chicken{
    
    
    private int id; //编号
    Chicken(int id){
    
    
        this.id = id;
    }
}

//缓存区
class SynContainer{
    
    
    //设置容器大小
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int conut = 0;

    //生产者生产放入产品
    public synchronized void push(Chicken chicken){
    
    
        if (conut == chickens.length){
    
    
            //通知消费者消费
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        chickens[conut] = chicken;
        conut++;
        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费取出产品
    public synchronized Chicken pop(Chicken chicken){
    
    
        if (conut == 0){
    
    
            //通知生产者生产
            try {
    
    
                this.wait();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }
        conut--;
        Chicken chicken1= chickens[conut];

        //吃完通知生产者生产
        this.notifyAll();
        return chicken1;
    }
}

5、线程池

public class ThreadPool {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        //1、创建线程池  newFixedThreadPool 参数为池子大小
        ExecutorService service = Executors.newFixedThreadPool(5);

        //2、执行线程
        service.submit(new MyThread01());
        service.submit(new MyThread01());
        service.execute(new MyThread01());
        Future submit = service.submit(new MyThread02());
        Integer number = (Integer) submit.get();
        System.out.println(number);

        //3、关闭线程池
        service.shutdown();

    }
}

class MyThread01 implements Runnable{
    
    

    @Override
    public void run() {
    
    
        System.out.println(Thread.currentThread().getName()+"MyThread01");
    }
}

class MyThread02 implements Callable {
    
    

    @Override
    public Integer call() throws Exception {
    
    
        System.out.println(Thread.currentThread().getName()+"MyThread02");
        return 11;
    }
}

注意点:

  • 线程池submit()方法 既可以执行Runnable接口 线程,也可执行 Callable 接口线程;
  • 线程池execute()方法只能执行 Runnable接口 线程

猜你喜欢

转载自blog.csdn.net/Start1234567/article/details/107790182