标签:nic buffer 抽象方法 失败 比较 提前 守护 开启 this
程序:指令和数据的集合
进程:程序的一次执行过程,是系统资源分配的基本单位
线程:是cpu调度和执行的单位
- 继承Thread
- ThreadImpl extends Thread
- 重写run()
- new Thread().start()
- 实现Runnable接口
- ThreadImpl implements Runnable
- 重写run()
- new Thread(new ThreadImpl( )).start()
- 实现Callable接口
- ThreadImpl implements Callable< call()的返回值 >
- 重写call()
- TestCallable t1 = new TestCallable()
- 创建执行服务 ExcutorService ser = Excutors.newFixedThreadPool(3);创建池子数量
- 提交执行 Future< bollean > result = ser.submit( t1 );
- 获取结果:result.get()
- 关闭服务:ser.shutdownNow();
? 推荐使用实现Runnable接口,避免了单继承的局限性,灵活方便,方便同一个对象被多个线程使用
(param)->expression [ 表达式 ]
(param)->statement [ 语句 ]
(param)->{statements}
lambda演化:外部类 --> 静态内部类 --> 局部类 --> 匿名内部类(借助接口或父类实现) --> lambda表达式【 (参数)-> { 方法体 } 】
定义:只包含一个抽象方法。
对于函数式接口,可以通过Lamda表达式来创建该接口的对象
新建:线程被创建,处于新建状态
就绪:线程对象调用start()方法开启线程,等待被cpu调度执行
运行:就绪状态的线程获得了cpu时间片,执行run()方法
阻塞: 线程放弃了对cpu的使用权,暂时停止运行
? 等待阻塞:线程调用wait()方法,进入等待阻塞
? 同步阻塞:线程获取synchronized同步锁失败,进入同步阻塞
? 其他阻塞:线程调用sleep()方法,进入阻塞状态
wait() | sleep() |
---|---|
会释放锁,属于Object类 | 不会释放锁,属于Thread类 |
只能在同步方法或者代码块里使用,不需要抛异常 | 可以在任何地方使用,需要抛出异常 |
需要notify()或notifyAll()唤醒 | 不需要唤醒,休眠之后自动退出阻塞 |
死亡:线程执行完了run()方法,或者因为异常而退出了run()方法,线程的生命周期结束
方法 | 说明 |
---|---|
setPriority( int ) | 更改线程的优先级 |
sleep( long ) | 指定的毫秒内休眠 |
join() | 等待线程终止 |
yield() | 让出cpu,再和其他线程一起争抢cpu |
interrupt() | 中断线程,别用 |
isAlive() | 测试线程是否处于活动状态 |
? 两个或多个线程,同时阻塞,都在等待某个资源的释放,而这个资源又被其他的线程占用,所以线程就一致阻塞,导致线程死锁。比如:现在有A、B两个线程,同时申请对方的资源,就会相互等待,线程一直处于等待状态,导致死锁。
? 一个同步代码块同时拥有 " 两个以上对象的锁 ",就可能发生 " 死锁 "。
线程死锁的四个条件:
破坏死锁:破坏四个条件中的一种, 互斥条件没办法破坏。
? 一、synchronized
? 为了保证数据在方法中被正确访问的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源其他线程必须等待,存在以下问题:
CopyOnWriteArrayList是线程安全的集合
? private关键字保证数据只能被方法访问,所以只需要对方法提出一套机制--->synchronized关键字,它包括两种用法,同步方法和同步代码块
二、Lock
? JDk5.0开始,提供了更强大的线程同步机制——通过显示显示定义同步锁。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,访问资源之前应先获得Lock对象。
? ReentrantLock(可重入锁)类实现了Lock,拥有和synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁和释放锁。
三、Lock和Synchronized的对比
Lock | Synchronized |
---|---|
显示锁,需要手动开启和关闭锁 | 隐式锁,出了作用域会自动释放锁 |
只能锁代码块 | 锁代码块和方法 |
JVM花费更少的时间去调度线程,性能好 |
java提供了几个方法解决了线程间的通信问题
方法名 | 描述 |
---|---|
wait() | 线程一直等待,直到其他线程通知,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 随机唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上所有调用wait()的线程,优先级别高的优先调度 |
这是一个线程同步问题,生/消共享同一个资源,仅有一个synchronized是不够的
package thread;
/**
*
* @author 柯神_
* @date 2020-11-29 17:17:17
* @Description 线程间通信,
* 生产者消费者问题
* 解决方式:一、管程法
* 二、信号灯法
*/
public class ThreadCommunication {
public static void main(String[] args) {
Buffer buffer = new Buffer();
Producer producer = new Producer(buffer);
Consumer consumer = new Consumer(buffer);
new Thread(producer).start();
new Thread(consumer).start();
}
}
/**
* 一、管程法
*/
//生产者
class Producer implements Runnable{
Buffer buffer;
public Producer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
buffer.push(new Product(i));
System.out.println("生产了" + i + "只鸡!");
}
}
}
//消费者
class Consumer implements Runnable{
Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了==>" + buffer.getP().id + "只鸡!");
}
}
}
//产品
class Product{
int id;
public Product(int id) {
this.id = id;
}
}
//缓冲区
class Buffer{
//定义一个容器大小
Product[] products = new Product[10];
//容器计数器
int count = 0;
//1.生产者生产产品
public synchronized void push(Product product){
/**
* 缓冲区满了,生产等待,通知消费
*/
if (count == products.length - 1){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 缓冲区未满,可以生产
*/
products[count] = product;
count++;
/**
* 有产品了
* 通知消费者消费
*/
this.notifyAll();
}
//2.消费者消费产品
public synchronized Product getP(){
/**
* 缓冲区空了,等待消费,通知生产
*/
if (count == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 缓冲区有产品,可以消费
*/
count--;
Product product = products[count];
/**
* 吃了,缓冲区没满了
* 通知生产者生产
*/
this.notifyAll();
return product;
}
}
信号灯法是通过标志位来实现
/**
* 二、信号灯法:通过标志位来实现
*/
package thread;
import lombok.SneakyThrows;
/**
* 二、信号灯法:通过标志位来实现
*/
public class ThreadCommunication2 {
public static void main(String[] args) {
TV tv = new TV();
Player player = new Player(tv);
Watcher watcher = new Watcher(tv);
new Thread(player).start();
new Thread(watcher).start();
}
}
//生产者 ==>演员录制
class Player implements Runnable{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
this.tv.play("录制快本中。。。");
} else {
this.tv.play("新闻联播录制...");
}
}
}
}
//消费者 ==>观众
class Watcher implements Runnable{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
tv.watch();
}
}
}
//产品 ==>小视频节目
class TV{
private String name;
private boolean flag = true; //标志位
/**
* 表演录制:观众等待
*/
@SneakyThrows
public synchronized void play(String name){
if (!flag){
wait();
}
System.out.println("演员表演了" + name);
this.notify();
this.name = name;
this.flag = !this.flag;
}
/**
* 观众观看:演员等待
*/
@SneakyThrows
public synchronized void watch(){
if (flag){
this.wait();
}
System.out.println("观众观看=======>" + name);
this.notifyAll();
this.flag = !this.flag;
}
}
背景:线程频繁地创建和销毁,对系统的性能影响很大
线程池:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,实现资源重复利用。
优点:
JDK5.0开始,提供了线程池相关的API:ExcutorService 和 Excutors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExcutor
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池。
package thread;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*
* @author 柯神_
* @date 2020-11-29 20:21:10
* @Description
* 测试线程池
*/
public class PoolTest {
public static void main(String[] args) {
//创建线程服务,线程池,参数为线程池的大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyPool());
service.execute(new MyPool());
service.execute(new MyPool());
service.execute(new MyPool());
service.shutdown();
}
}
class MyPool implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
标签:nic buffer 抽象方法 失败 比较 提前 守护 开启 this
原文地址:https://www.cnblogs.com/qqkkOvO/p/14057806.html