JavaSEDemo17线程池调度器与ThreadLocal

简介

  • 本文是2021/04/14整理的笔记
  • 赘述可能有点多,还请各位朋友耐心阅读
  • 本人的内容和答案不一定是最好最正确的,欢迎各位朋友评论区指正改进

易错练习题

练习1

class MyValue{
private int data;
public void m(){
int result = 0;
result += 2;
data += 2;
System.out.println(result + “ ” + data);
}
}
class MyThread extends Thread{
private MyValue mv;
public MyThread(MyValue mv){
this.mv = mv;
}
public void run(){
synchronized(mv){
mv.m();
}
}
}
public class TestMyThread{
public static void main(String args[]){
MyValue mv = new MyValue();
Thread t1 = new MyThread(mv);
Thread t2 = new MyThread(mv);
Thread t3 = new MyThread(mv);
t1.start();
t2.start();
t3.start();
}
}
问:
1) 写出该程序输出的结果
2) 如果把run 方法中的synchronized 代码块去掉,而是直接调用
public void run(){
mv.m();
}
则会产生同步问题。如果不允许改动MyThread 类,应该如何修改,以保证代码
同步?

(1)
2 2
2 4
2 6
(2)
答案:
向MyValue类中的m()方法中加锁。

练习2 代码改错

class MyThread1 implements Runnable{
public void run() {
for(int i = 0; i<100; i++){
this.sleep((int)(Math.random()*1000));
System.out.println(“hello”);
}
}
}
class MyThread2 extends Thread{
public void run() throws Exception {
for(int i = 0; i<100; i++){
this.sleep((int)(Math.random()*1000));
System.out.println(“world”);
}
}
}
public class TestMyThread{
public static void main(String args[]){
Runnable t1 = new MyThread1();
Thread t2 = new MyThread2();
t1.start();
t2.start();
}
}

答案
改正后的代码为:

class MyThread1 implements Runnable{
    @Override
    public void run() {
        for(int i = 0; i<100; i++){
            try {
                Thread.sleep((int)(Math.random()*1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("hello");
        }
    }
}
class MyThread2 extends Thread{
    @Override
    public void run()  {
        for(int i = 0; i<100; i++){
            try {
                this.sleep((int)(Math.random()*1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("world");
        }
    }
}
public class TestMyThread{
    public static void main(String args[]){
        //Runnable t1 = new MyThread1();
        Thread t1 = new Thread(new MyThread1());
        Thread t2 = new MyThread2();
        t1.start();
        t2.start();
    }
}

练习3

class MyThread extends Thread{
private int data;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
Public synchronized void show(){
}
public synchronized void run(){
for(int i = 0; i<100; i++){
System.out.println(data);
try {
Thread.sleep((int)(Math.random()*1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestThread {
public static void main(String[] args) throws Exception{
MyThread t1 = new MyThread();
t1.setData(100);
t1.start();
Thread.sleep(5000);
t1.  (200);
}
}
线程启动以后,主方法中调用setData 方法,能否修改t1 线程中的data
值?如果可以,应该如何修改代码,使得在t1 运行过程中,其data 属性不会被
修改?如果不可以,画出线程运行的状态示意图。

答案
可以
在setData方法定义时添加synchronized关键字。
同步方法的锁是this,一旦锁住一个方法,就锁住了所有的同步方法。所以setData的数据在for循环运行时无法设置。

练习4

完成下列程序要求
有个Student 类,代码如下:
class Student{
String name;
int age;
//构造方法和get/set 方法请自行补充完成//学生问老师问题
public void ask(Teacher t){
t.answer(this);//调用老师的answer 方法
}
public void study(){
System.out.println(name + “ study”);
}
public void doHomework(){
System.out.println(name +do homework”);
}
}
定义Teacher 接口
interface Teacher{
void answer(Student stu);
}
给出一个Teacher 接口的实现类。该实现类实现answer 方法的时候,要求每次
学生调用老师的answer 方法时,都创建一个新线程,该线程调用学生的学习方
法和做作业方法。

答案

class TeacherImp extends Thread implements Teacher{
    Student student = new Student();
    @Override
    public void run() {
        student.doHomework();
        student.study();
    }
    @Override
    public void answer(Student stu) {
       TeacherImp teacherImp = new TeacherImp();
       teacherImp.start();
    }
}

练习5 读程序写结果

class Computation implements Runnable {
private int result;
public Computation() {
}
public void run() {
countprint(this.result);
}
public synchronized void countprint(int result) {
result = result + 2;
System.out.println(result);
}
}
public class TestComputation{
public static void main(String args[]){
Runnable target = new Computation();
new Thread(target).start();
new Thread(target).start();
new Thread(target).start();
new Thread(target).start();
}
}
写出该程序的运行结果。

答案
2
2
2
2
解析:
countprint方法中,只是向局部变量result中赋值,并不会影响到成员变量result。

多线程经典问题:生产者和消费者

  • 它描述一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品
  • 4个条件
    (1) 生产者仅仅在仓储未满时生产,仓满则停止生产
    (2) 消费者仅仅在仓储有产品时才会消费,仓空则等待
    (3) 当消费者发现仓储没产品时会通知生产者生产
    (4) 生产者在生产出产品时,应该通知消费者去消费

仓库类

public class WareHouse{
//商品数量
private int goods = 0;
public int getGoods(){
}
public void setGoods(int goods){
this.goods = goods;
}
//生产产品的方法
public synchronized void produceGoods(){
while(goods>10){
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("生产产品,产品数量=" + goods);
count++;
notify();
}
//购买产品的方法
public synchronized void consumeGoods(){
while(goods>10){
try{
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("购买产品,产品数量=" + goods);
}
}

Produce类

public class Produce implements Runnable{
    WareHouse wareHouse;
    public Produce(WareHouse wareHouse) {
        this.wareHouse = wareHouse;
    }

    @Override
    public void run() {
        while (true) {
            wareHouse.produceData();
        }
    }
}

Consume类

public class Consume implements Runnable{
    WareHouse wareHouse;
    public Consume(WareHouse wareHouse) {
        this.wareHouse = wareHouse;
    }

    @Override
    public void run() {
        while (true) {
            wareHouse.consumeData();
        }
    }
}

Test类

public class Test {
    public static void main(String[] args) {
        //仓库对象
        WareHouse wareHouse = new WareHouse();
        //生产者对象
        Produce produce = new Produce(wareHouse);
        //消费者对象
        Consume consume = new Consume(wareHouse);
        Thread thread1 = new Thread(produce);
        Thread thread2 = new Thread(consume);
        thread1.start();
        thread2.start();
    }
}

线程池调度器

重要核心接口和类:

接口: Executor ExecutorService ScheduleExecutorService
工具类:Executors
在这里插入图片描述

Executors的工厂方法及其构造器说明

  1. newFixedThreadPool(int nThreads)
    创建固定数目的线程池,参数表示数目
  2. newCachedThreadPool 最常用
    创建一个可以缓存的线程池
  3. newSingleThreadExecutor
    创建一个单线程化的Executor
  4. newScheduledThreadPool(int corePoolSize)
    创建一个支持执行定时及周期性任务的线程池,多数情况下可以由Timer类替换。

线程池的使用过程

  1. 用 Executors 的 newXXXThreadPool()返回 ExecutorService 类型的线程池
  2. 调用该线程的execute( )或submit( )方法或者invokeXXX( )方法来执行参数传入的任务。
  3. 调用线程的shutdown()方法来关闭线程,让其处于闲置状态。

execute(Runnable)

  • 语法格式: void execute(Runnable command)

submit方法的2种重载形式

  • submit(Runnable)
    语法格式:Future<?> submit(Runnable task)
  • submit(Callable)
    语法格式:Future< T > submit(Callable< T > task)
  • 二者的区别:Runnable接口的run方法没有返回值,Callable接口的call方法有返回值

invokeXXX方法

invokeAny方法

  • < T > T invokeAny(Collection<? extends Callable> tasks)throwsInterruptedException,ExecutionException
    执行给定的任务,如果某个任务已成功,则返回执行结果。

invokeAll方法

ThreadLocal

定义

作用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香鱼嫩虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值