文章目录
简介
- 本文是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的工厂方法及其构造器说明
- newFixedThreadPool(int nThreads)
创建固定数目的线程池,参数表示数目 - newCachedThreadPool 最常用
创建一个可以缓存的线程池 - newSingleThreadExecutor
创建一个单线程化的Executor - newScheduledThreadPool(int corePoolSize)
创建一个支持执行定时及周期性任务的线程池,多数情况下可以由Timer类替换。
线程池的使用过程
- 用 Executors 的 newXXXThreadPool()返回 ExecutorService 类型的线程池
- 调用该线程的execute( )或submit( )方法或者invokeXXX( )方法来执行参数传入的任务。
- 调用线程的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
执行给定的任务,如果某个任务已成功,则返回执行结果。