java线程笔记(锁线程通讯线程池)
Posted 皓洲
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程笔记(锁线程通讯线程池)相关的知识,希望对你有一定的参考价值。
java线程
文章目录
创建线程方式一:继承Thread类
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
//总结:注意,线程开启不一定立即执行,由CPU调度执行
public class Test extends Thread
@Override
public void run()
for(int i=0;i<20;i++)
System.out.println("我在看代码===="+i);
public static void main(String[]args)
//main线程,主线程
//创建一个线程对象
Test test=new Test();
//调用start()方法开启线程
test.start();
for(int i=0;i<200;i++)
System.out.println("I am eating lunch===="+i);
创建线程方式2:实现runnable接口
//创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法。
public class Test implements Runnable
@Override
public void run()
for(int i=0;i<20;i++)
System.out.println("我在看代码===="+i);
public static void main(String[]args)
//main线程,主线程
//创建一个线程对象
Test test=new Test();
//船家女线程对象,通过线程对象来开启我们的线程,代理
new Thread(test).start();
for(int i=0;i<200;i++)
System.out.println("I am eating lunch===="+i);
对比
- 继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
- 实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.strat()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用。
多个线程操作同一个对象:买火车票
import java.util.*;
//多个线程操作同一个对象:买火车票
//发现问题:多线程操作同一个资源的情况下,线程不安全,数据紊乱
public class Test implements Runnable
//票数
private int ticketNums = 10;
@Override
public void run()
while(true)
if(ticketNums<=0)
break;
try
Thread.sleep(200);
catch (InterruptedException e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+"--->拿到了第"+ticketNums--+"票");
public static void main(String[]args)
Test ticket = new Test();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
输出结果:
出现了两个第7票和第0票第-1票。说明存在并发问题。
同步锁synchronized
-
synchronized方法控制”对象“的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
缺点:若将一个大的方法声明为synchronized将会影响效率。
import java.util.*;
//多个线程操作同一个对象:买火车票
//发现问题:多线程操作同一个资源的情况下,线程不安全,数据紊乱
public class Test implements Runnable
//票数
private int ticketNums = 10;
@Override
public void run()
while(true)
if(ticketNums<=0)
break;
try
Thread.sleep(200);
catch (InterruptedException e)
e.printStackTrace();
buy();
//加锁解决冲突
public synchronized void buy()
if(ticketNums>0)
System.out.println(Thread.currentThread().getName()+"--->拿到了第"+ticketNums--+"票");
public static void main(String[]args)
Test ticket = new Test();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
结果:
Lock锁
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
//多个线程操作同一个对象:买火车票
//发现问题:多线程操作同一个资源的情况下,线程不安全,数据紊乱
public class Test implements Runnable
//票数
private int ticketNums = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run()
while(true)
try
lock.lock();
if(ticketNums<=0)
break;
try
Thread.sleep(200);
catch (InterruptedException e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+"拿走了第"+ticketNums--+"张票");
finally
lock.unlock();
public static void main(String[]args)
Test ticket = new Test();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
结果:
发现票全是黄牛拿走的,因为黄牛抢到了这把锁,一直买票,最后才释放。
synchronized与Lock的对比
- Lock是显示锁(手动开启或关闭锁,别忘记关闭锁)synchronized是隐式锁,除了作用域自动释放
- Lock只有代码块锁,synchronized由代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。斌且具有很好的拓展性(提供更多子类)
- 优先使用顺序:
- Lock>同步代码块(已经进入了方法体,分配了相应的资源)> 同步方法(在方法体之外)
线程通信
- 应用场景:生产者和消费者问题
- 假设仓库中只能放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费。
- 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,知道仓库中的产品被消费者取走为止。
- 如果仓库中有产品,则消费者可以将产品取走消费,否则停止消费并等待,知道仓库中再次放入产品为止。
- 这是一个线程同步问题,生产者和消费者共享一个资源,并且生产者和消费者之间互相依赖,互为条件。
- 对于生产者,没有生产商品之前,要通知消费者等待,而产生了商品之后,又要马上通知消费者消费
- 对于消费者,再消费之后,要通知生产者 已经结束消费,需要产生新的产品以供消费。
- 再生产者问题中,仅有synchronized是不够的
- synchronized可阻止并发更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之间的消息传递(通讯)
并发协作模型“生产者/消费者模式”—>管程法
- 生产者:负责生产数据得模块(可能是方法,对象,进程、线程)
- 消费者:负责处理数据得模块(可能是方法,对象,进程、线程)
- 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”
生产者将生产好的数据放入缓冲区,消费者冲缓冲区拿出数据。
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
//生产者消费者问题
public class Test
public static void main(String[] args)
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
//生产者
class Productor extends Thread
SynContainer container;
public 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 extends Thread
SynContainer container;
public Consumer(SynContainer container)
this.container = container;
//消费
@Override
public void run()
for (int i = 0; i < 100; i++)
System.out.println("消费了---->第"+container.pop().id+"只鸡");
//产品
class Chicken
int id;
Chicken(int id)
this.id=id;
//缓冲区
class SynContainer
//需要一个容器大小
Chicken[] chickens = new Chicken[10];
//容器计数器
int cnt=0;
//生产者放入产品
public synchronized void push(Chicken chicken)
while(cnt>=10)
//通知消费者消费,生产等待
try
this.wait();
catch (InterruptedException e)
e.printStackTrace();
//没有满,则生产产品
chickens[cnt++]=chicken;
this.notifyAll();
//消费者消费产品
public synchronized Chicken pop()
//判断是否能消费
while(cnt<=0)
//等待生产者生产
try
this.wait();
catch (InterruptedException e)
e.printStackTrace();
//如果可以消费
cnt--;
Chicken chicken = chickens[cnt];
//吃完了,通知生产者生产
this.notifyAll();
return chicken;
结果:
使用线程池
- 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的交通工具。
- 好处:
- 提高相应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
- 便于线程管理
- corePoolSize:核心池的大小
- maximuPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
- TimeUnit:时间单位
- BlockingQueue< Runable > workqueue :等待队列
- ThreadFactory:线程工厂
- RejectedExecutionHandler:拒绝策略
线程池
/**
- corePoolSize:核心池的大小
- maximuPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
- TimeUnit:时间单位
- BlockingQueue< Runable > workqueue :等待队列
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
/**
- newCachedThreadPool()
- corePoolSize:核心线程数为0
- maximuPoolSize:最大线程数为int范围内最大的数2^31-1
- keepAliveTime:存活时间 60
- TimeUnit:单位 秒
- BlockingQueue< Runable > workqueue :等待队列为SynchronousQueue 大小为1
*/
public static ExecutorService newCachedThreadPool()
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
/**
- newCachedThreadPool()
- corePoolSize:核心线程数为n
- maximuPoolSize:最大线程数为n
- keepAliveTime:存活时间 0
- TimeUnit:单位 min
- BlockingQueue< Runable > workqueue :LinkedBlockingQueue 大小为int范围内最大的数2^31-1
*/
public static ExecutorService newFixedThreadPool(int nThreads)
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
/**
- newCachedThreadPool()
- corePoolSize:核心线程数为1
- maximuPoolSize:最大线程数为1
- keepAliveTime:存活时间 0
- TimeUnit:单位 min
- BlockingQueue< Runable > workqueue :LinkedBlockingQueue 大小为int范围内最大的数2^31-1
*/
public static ExecutorService newSingleThreadExecutor()
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
自定义线程池
/**
- newCachedThreadPool()
- corePoolSize:核心线程数为10
- maximuPoolSize:最大线程数为20
- keepAliveTime:存活时间 0
- TimeUnit:单位 sec
- BlockingQueue< Runable > workqueue :ArrayBlockingQueue 大小为10
*/
//自定义线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,
20,
0L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(10));
提交优先级
- 核心线程
- 等待队列
- 非核心线程
以上是关于java线程笔记(锁线程通讯线程池)的主要内容,如果未能解决你的问题,请参考以下文章
Python之路第一课Day9--随堂笔记之二(进程线程协程篇)
Python学习笔记——进阶篇第九周———线程进程协程篇(队列Queue和生产者消费者模型)
Java 并发编程线程池机制 ( 线程池执行任务细节分析 | 线程池执行 execute 源码分析 | 先创建核心线程 | 再放入阻塞队列 | 最后创建非核心线程 )