什么是线程、进程、多线程?
- 线程: 是Java中执行代码的一条执行路径。
线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行
线程基本上是轻量级的进程,它负责在单个程序里执行多任务。通常由操作系统负责多个线程的调度和执行。 - 进程:每个正在系统上运行的程序都是一个进程,是操作系统调度的执行单元。
- 多线程:在一个进程中,有多条不同的执行路径,并行执行,目的为了提高程序的效率
在一个进程中有一个主线程作为程序的入口
线程分类
用户线程:用户进程可以通过setDeamon设置为守护进程,执行业务代码。
守护线程:低级别线程,主要是为用户线程进行服务的。
线程的实现方式
【 https://blog.csdn.net/qq_34898847/article/details/84858048 】
主要就是集成Thread 类 , 实现Runnable借口,匿名内部类, 线程池等方式。
同步、异步概念
同步: 代码从上往下执行 (HTTP借口:请求-》响应)
异步: 不同线程之间的执行不会互相影响
线程的运行状态
新建: new Thread()
就绪: 调用start()方法,等待CPU调度分配执行权限
运行: 执行run()方法
阻塞: 当调用sleep 、 wait 、join 方法等的时候
死亡:run方法执行完毕,销毁
join方法: 释放CPU执行权, 若 A、B 线程, A.join(), 则A线程先执行
yield()方法: A.yield() : 则B先执行
线程安全
- 当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
- 将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,让后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
内置的锁
Java提供了一种内置的锁机制来支持原子性
内置锁使用synchronized关键字实现,synchronized关键字有两种用法
(1)修饰需要进行同步的方法(在方法上修饰synchronized 称为同步方法)
(2)同步代码块
synchronized(同一个数据){
可能会发生线程冲突问题
}
同步的前提:
1,必须要有两个或者两个以上的线程
2,必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。
他是可重入锁的:获得了锁, 可以进行锁传递
总结:
synchronized 修饰方法使用锁是当前this锁。
synchronized 修饰静态方法使用锁是当前类的字节码文件
多线程死锁
有A/B线程
A需要先获取obj锁,然后获取this锁
B需要先获取this锁,再获取obj锁
当A拥有obj锁,没有释放,等待获取this锁;B获取了this锁,没有释放,等待获取obj锁;出现死锁
解决方法:
注意枷锁的顺序;
设置加锁的时限(放弃请求锁和释放自己的锁)
线程安全解决办法?
使用多线程之间同步synchronized或使用锁(lock)
多线程应用场景?
答:主要能体现到多线程提高程序效率。
举例: 迅雷多线程下载、数据库连接池、分批发送短信等。
什么是多线程之间通讯?
多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。
wait、notify方法
1.因为涉及到对象锁,他们必须都放在synchronized中来使用. Wait、Notify一定要在synchronized里面进行使用。
2.Wait必须暂定当前正在执行的线程,并释放资源锁,让其他线程可以有机会运行
3. notify/notifyall: 唤醒因锁池中的线程,使之运行
注意:一定要在线程同步中使用,并且是同一个锁的资源
wait与sleep区别
对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备
获取对象锁进入运行状态。
Lock锁
在 jdk1.5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来实现锁功能,Lock 接口提供了与 synchronized 关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。
Lock lock = new ReentrantLock();
lock.lock();
try{
//可能会出现线程安全的操作
}finally{
//一定在finally中释放锁
//也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
lock.ublock();
}
Lock与synchronized 关键字的区别
Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
Lock 接口能被中断地获取锁 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回
Condition用法
类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。
Condition condition = lock.newCondition();
res. condition.await(); 类似wait
res. Condition. Signal() 类似notify
什么是Threadlocal
ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
API:
• void set(Object value)设置当前线程的线程局部变量的值。
• public Object get()该方法返回当前线程所对应的线程局部变量。
• public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
• protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。
ThreadLoca实现原理:
ThreadLoca通过map集合
Map.put(“当前线程”,值);
多线程有三大特性
原子性、可见性、有序性
- 什么是原子性?
即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 - 什么是可见性?
当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 - 什么是有序性?
程序执行的顺序按照代码的先后顺序执行。
生产者、消费者案列
/**
* 生产对象
* @author xzb
*
*/
class Demo {
String name;
String age;
Boolean flag = false;
}
/**
* 生产者
* @author xzb
*
*/
class Provider extends Thread{
private Demo demo;
public Provider(Demo demo) {
this.demo = demo;
}
public void run() {
int count = 0; // 0 1 0 1 0 1
while (true) {
synchronized (demo) {
try {
Thread.sleep(1000);
if (demo.flag) {
demo.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (count == 0) {
// 生产对象
demo.name = "小兵";
demo.age ="23";
} else {
demo.name = "小王";
demo.age ="22";
}
count = (count+1) %2;
demo.flag = true;
demo.notify();
}
}
}
}
/**
* 消费者
* @author xzb
*
*/
class Consumer extends Thread{
private Demo demo;
public Consumer(Demo demo) {
this.demo = demo;
}
@Override
public void run() {
while(true) {
synchronized (demo) {
if (!demo.flag) {
try {
demo.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者:" +demo.name +","+demo.age);
demo.flag = false;
demo.notify();
}
}
}
}
/**
* wait()和notify() 方法必须持有相同的对象锁, 必须在synchronized同步中, (他们都在object对象中, 需要锁对象)
* wait() 方法 阻塞当前线程, 并且释放当前所持有的锁
* notify() 唤醒对象锁池的等待线程
* join() 不会释放锁, 可不再同步代码块中
* sleep() 该方法属于Thread,wait()方法属于Object,线程不会释放对象锁。
*
* @author xzb
*
*/
public class Test002 {
public static void main(String[] args) {
// 共享资源
Demo demo = new Demo();
Provider p = new Provider(demo);
Consumer c = new Consumer(demo);
p.start();
c.start();
}
}
基于队列方式实现生产者与消费者
/**
* 队列的生产者
* @author xzb
*
*/
class ProviderQueue implements Runnable{
private BlockingQueue<String> blockQueue;
private volatile boolean FLAG = true;
private AtomicInteger count = new AtomicInteger();
public ProviderQueue(BlockingQueue<String> blockQueue) {
this.blockQueue = blockQueue;
}
public void run() {
System.out.println(Thread.currentThread().getName() + "生产者开始生产。。。");
while (FLAG) {
String data = count.incrementAndGet() + "";
try {
// 存放队列
boolean offer = blockQueue.offer(data, 2, TimeUnit.SECONDS);
if (offer) {
System.out.println(Thread.currentThread().getName()+",生产队列:" + data + "成功。。");
} else {
System.out.println(Thread.currentThread().getName()+",生产队列:" + data + "失败。。。");
}
Thread.sleep(1000);// 为了看效果
} catch (Exception e) {
// TODO: handle exception
}
}
System.out.println(Thread.currentThread().getName() + ",生产者停止生产。。。");
}
public void stop () {
this.FLAG = false;
}
}
class ConsumerQueue implements Runnable {
private BlockingQueue<String> blockQueue;
private volatile boolean FLAG = true;
public ConsumerQueue(BlockingQueue<String> blockQueue) {
this.blockQueue = blockQueue;
}
public void run() {
System.out.println(Thread.currentThread().getName() + "消费者启动消费。。。");
while (FLAG) {
try {
String data = blockQueue.poll(2, TimeUnit.SECONDS);
if (data == null) {
this.FLAG = false;
System.out.println("消费者超过2秒还未获取到队列消息。。。");
return ;
}
System.out.println("消费者消费消息data: " + data);
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
public class TestDemo {
public static void main(String[] args) {
BlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<String>();
ProviderQueue providerQueue = new ProviderQueue(linkedBlockingQueue);
ConsumerQueue consumerQueue = new ConsumerQueue(linkedBlockingQueue);
Thread p1 = new Thread(providerQueue);
Thread c1 = new Thread(consumerQueue);
p1.start();
c1.start();
// 为了模拟效果, 10秒后停止生产
try {
Thread.sleep(10000);
providerQueue.stop();
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("线程结束。。。");
}
}