如何创建线程,多线程下又如何上锁保护公共资源?

文章详细介绍了Java中创建线程的五种方法,包括继承Thread类、实现Runnable接口、使用匿名类和Lambda表达式、利用Callable接口以及通过线程池。此外,还深入探讨了多线程环境下对共享资源的保护,如使用synchronized关键字和Lock接口,以及如何通过Condition实现选择性通知的等待/通知模式。文章通过实例展示了如何在并发编程中实现复杂的线程通信和同步策略。
摘要由CSDN通过智能技术生成

目录

一、创建线程几种方法:

1、继承thread类,重写run方法     2、实现runnable接口,重写run方法

3、使用匿名类 或 lamda表达式 让代码更简洁

4、Callable 接口

5、使用线程池创建线程

二、多线程下,需要上锁来保护公用资源(synchronized;lock)


一、创建线程几种方法:

1、继承thread类,重写run方法     2、实现runnable接口,重写run方法

//继承Thread类
public class MyThread extends Thread{
  public void run(){
  //重写run方法
  } 
}
//实现Runnable接口
public class MyThread2 implements Runnable {
  public void run(){
  //重写run方法
  }
}

public class Main {
  public static void main(String[] args){
    new MyThread().start();//创建并启动线程

        Thread t2 = new Thread(MyThread2);
        t2.setName("线程2");
        t2.start();
new Thread(MyThread2).start();
new Thread(MyThread2,"线程2").start();
  }
}

Java 是单继承编程语言,继承是十分宝贵的,所以一般不使用这种方法:继承Thread类 

3、使用匿名类 lamda表达式 让代码更简洁

//使用匿名类
new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 100; i++) {
                        share.incr();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

//lamda表达式
new Thread(() -> {
	System.out.println(Thread.currentThread().getName() + "\t上完自习,离开教室");
}, "AA").start();
 

4、Callable 接口

Runnable 缺少的一项功能是,当线程终止时(即 run()完成时),我们无法使线程返回结果。为了支持此功能,Java 中提供了 Callable 接口Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。

需要使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值。

FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。

package com.atguigu.java2;

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

//1.创建一个实现Callable的实现类
class NumThread implements Callable {
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        //把100以内的偶数相加
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}

public class ThreadNew {
    public static void main(String[] args) {    
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();

        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);

        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();

        try {
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}


//————————————————————————————————————————————————————————
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"执行Runnable");
        }).start();
        
        FutureTask<String> task = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + "使用Callable接口");
            return "Callable接口返回值";
        });
        
        new Thread(task).start();
        System.out.println("Callable返回值:" + task.get());
    }
}

5、使用线程池创建线程

二、多线程下,需要上保护公用资源(synchronized;lock

  1. synchronized是java关键字,内置,而lock不是内置,是一个类,可以实现同步访问且比 synchronized中的方法更加丰富,并且可以支持多个相关联的对象 Condition
  2. synchronized不会手动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)
  3. lock等待锁的线程会相应中断,而synchronized不会相应,只会一直等待
  4. 通 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到
  5. Lock 可以提高多个线程进行读操作的效率(当多个线程竞争的时候)锁会出现死锁,需要在 finally 块中释放锁)

关键字 synchronized 与 wait()/notify() 这两个方法一起使用可以实现等待/通知模式
用 notify()通知时,JVM 会随机唤醒某个等待的线程

public synchronized void incr() throws InterruptedException {
        // 操作:判断、干活、通知
        if (number != 0) {
            this.wait();	
        }
        number++;
        System.out.print(Thread.currentThread().getName()+"::"+number);
        // 唤醒其他线程,注意这里的通知是随机的,就是只能通知全部
        this.notifyAll();
    }

Lock 实现可重入锁

使用 Condition 类可以进行选择性通知: Condition有以下两个方法await()和signal()

// 创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
//Lock 接口中的 newContition() 方法返回 Condition 对象,Condition 类也可以实现等待/通知模式
private Condition condition = lock.newCondition();

try {
    //上锁
    lock.lock();
//功能操作:————————————————————————————————————————
    //使用 Condition 类可以进行选择性通知: Condition有以下两个方法await()和signal()
    //判断循环await()、干活、通知signal()
        // 判断
            while (number != 0) {
                condition.await();
            }
            // 干活
            number++;
            System.out.print(Thread.currentThread().getName() + "::" + number + "--->");
            // 通知
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
//——————————————————————————————————————————
}finally {
    //解锁
    lock.unlock();
}

案列:启动三个线程,按照如下要求: AA打印5此,BB打印10次,CC打印15次,一共进行10轮

由于需要选择性通知唤醒进程,因此使用lock

class Share{
    private int flag = 1;

    private Lock lock = new ReentrantLock();
    // 创建三个Comdition对象,为了定向唤醒相乘
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();

    public void Aprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断
            while(flag!=1) {
                c1.await();
            }
            // 干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 2; //修改标志位,定向唤醒 线程b
            // 唤醒
            c2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
    public void Bprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断
            while(flag!=2) {
                c2.await();
            }
            // 干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 3; //修改标志位,定向唤醒 线程b
            // 唤醒
            c3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }

    public void Cprint(int loop) {
        //上锁
        lock.lock();
        try{
            // 判断,这个唤醒最好在while里头执行,防止出现虚假唤醒
            //由于 wait() 方法使线程在哪里睡就在哪里醒,所以接下来C在执行时不会再通过 if 判断
            while(flag!=3) {
                c3.await();
            }
            // 干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + " ::本次第" + i + "次打印,是第" + loop+ "次循环");
            }
            flag = 1; //修改标志位,定向唤醒 线程b
            // 唤醒
            c1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    }
}

public class CustomInterThreadCommunication {
    public static void main(String[] args) {
        Share share = new Share();
//在主函数里创建线程:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    share.Aprint(i);
                }

            }
        },"A").start();
        
// new Thread(r, "A").start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    share.Bprint(i);
                }
            }
        },"B").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    share.Cprint(i);
                }

            }
        },"C").start();
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值