多线程
什么是多线程 ?
多线程的目的就是提高程序的效率 进程是线程的集合 线程是进程的一条执行路径
创建线程的几种方式?
- 继承Thread类
- 实现Runnable接口
- 使用匿名内部类
- Callback
然后一般在企业开发是使用线程池。
常用API (方法)
获取子线程的id name 在run方法里面 这是要用线程对象去调用的 getId() getName()
获取主线程的id name Thread.currentThread.getId() Thread.currentThread.getName()
Thread.sleep();
Thread.currentThread.stop() // 这个方法已经过时了 不推荐使用 因为不安全。
同步和异步
同步:在代码从上往下执行就叫做同步 执行这段代码必须要等上一段代码执行完才可以执行
异步:不需要等代码执行完就可以去执行
守护线程和非守护线程
守护线程 : 如果主线程被销毁的话,守护线程一起跟着销毁,比如:gc线程
非守护线程: 主线程销毁, 非守护线程不变仍然执行,比如:自己写的用户线程。
当然非守护线程可以设置为守护线程 t.setDamon(true); // 设置该线程为守护线程与主线程一起。
线程的状态转换
Join方法
意思就是让其他线程先执行 谁调用这个方法谁就让出资源给其他线程先执行。
Wait()方法 notify()和stop()方法
Stop就是直接杀死线程
Wait就是使当前线程阻塞 当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。
Notify 就是唤醒当前阻塞的线程 当 notify/notifyAll() 被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized 代码块的代码或是中途遇到wait() ,再次释放锁。
分批道处理 ,也就是多开几个线程 然后将任务分成多个模块 分别取执行。
多线程的三大特性:
原子性:独一无二、一致性、保证线程的安全问题
可见性:java内存模型
有序性:join() wait notify (多线程之间的通信)
线程安全问题:有多个线程同时对一个全局变量去做赋值操作,或者说写的操作的时候。
也就是说多个线程共享同一资源时,导致错误。
解决线程安全问题:
线程同步,保证数据的原子性
方法1、synchronize() -----自动
方法2、lock jdk1.5 的并发包 -----手动
Synchronize 这个效率低因为资源在进入获取锁的对象的时候会出现锁的竞争问题。也可能有线程不释放锁,而产生死锁问题。
同步代码块
Synchronize{
//里面是要执行的方法
}
使用同步函数解决线程安全问题
Private Synchronize void run(){
//切记同步函数是的是this锁。
}
多个线程需要使用同一把锁才能实现线程同步。 因为 锁只有一把,当一个线程拿到了锁的时候,其他线程就要等待,这才是实现同步,而当锁不一样的话,那就会出现多个线程同时拿到锁访问共享资源。
静态同步函数
Public synchronize void save(){
//静态同步函数使用的是当前的字节码文件作为锁 文件名.class
}
另外这个锁和分布式锁,高并发,jvm没什么大的联系。
ThreadLoca
就是每个线程都有一个资源去自己操作
就像这样: Map集合 key存的是线程 value存的是变量 这个时候变量也不能说是共享了吧。
就是将 共享变量放到每一个线程里面去做局部变量
他有一个set() 和一个get();
伪代码写一下
当然这个map要所有线程都可以访问得到的
Map map = new Map();
Public boolean set(Object o){
//将当前线程的id作为key 变量作为value
map.(Thread.currentThread.getId(),” o ”);
}
//将当前线程作为id直接取出来。
Public Object get(){
Return map.get(Thread.currentThread.getId());
}
Java内存模型
Volatile
强制刷新将主内存数据刷新到本地内存 保证数据的一致性。
AtomicInteger 也是保证线程的安全问题 volatile也会出现并发问题。
解释一下为什么会出现以下情况 (这里你可能看不懂 这是一个例子)
不是一千一千的 因为每一个线程都是+1000
因为那十个线程没有排队都是抢着执行run方法 并同时在修改那个count数据
画个图
保证生产者生产一个,消费者消费一个 就这样一直下去 而不是生产者生好多个,然后消费者消费好多一样的。
效果如下:
package thread;
/**
* 生产者消费者模式
* 实现生产者生产一个消费者消费一个 这样交替执行。
* @author 蒋子文
*
*/
public class ThreadConPro {
public static void main(String[] args) {
Res res = new Res();
Out out = new Out(res);
In in = new In(res);
in.start();
out.start();
}
}
//读的操作 false 写 true 读
class Out extends Thread{
private Res res;
public Out(Res res) {
this.res = res;
}
//run方法
@Override
public void run() {
while(true) {
synchronized (res) {
if(!res.flag) {
try {
res.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
System.out.print(res.userName+" ");
System.out.println(res.sex);
res.notify();
res.flag = false;
}
}
}
}
}
//写的操作 也就是生产者
class In extends Thread{
private Res res;
public In(Res res) {
this.res = res;
}
//重写run方法
@Override
public void run() {
// TODO Auto-generated method stub
int count = 0;
while(true) {
synchronized (res) {
if(res.flag) {
try {
res.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else {
if(count == 1) {
res.userName = "JZWen";
res.sex = "男";
}else {
res.userName = "NanN";
res.sex = "女";
}
count = (count+1) % 2;
res.notify();
res.flag = true;
}
}
}
}
}
class Res{
public String userName;
public String sex;
public boolean flag = false;
}
使用jdk1.5的并发包 lock加锁
使用lock锁需要在 锁的对象里面加一个东西,这样lock才可以去调用lock.lock();
这样就已经确定了 锁就是res
Wait() 和 notify() 不可以再这里面是使用
需要这样使用
下面这个代码写错了 记住 newCondition必须是同一个对象 引用需要一样 很好理解这是为什么
Res.lock.lock(); 这样就加锁了
Res.lock.unlock(); 这样就释放锁了。
停止线程
Stop() 已经过期了 不安全 不能回滚 程序执行到一般的话 然后stop()程序会挂掉。
用一个中断函数去抛出一个异常。
Vector和ArrayList
Vector是线程安全的 但是他的效率低
在源码上 Vector的add方法上加了Synchronize 同步关键字 所以也很容易理解他为什么是效率低的。
ArrayList 的线程不安全但是效率高
Add()是这样的 Boolean add () ;
HashMap和HashTable
HashTable 是线程安全的 他的put方法 和 get方法 加了一个Synchronize 效率低
HashMap 是线程不安全的 效率高
然后又有一个类出来ConcurrentHashMap 采用分段锁 线程安全而且效率高
ConcurrentHasMap 还是使用HashTable 默认将其分成16段,当然每一段之间也是使用HashTable 意思就是每一个 然后每一段之间使用的是同一把锁 不同段之间使用的是不同的锁。
这样子也是提高了效率的。
currentHashMap
源码分析
https://blog.csdn.net/u010723709/article/details/48007881
CountDownLatch java并发包
就是当这个在创建的时候
CountDownLatch cont = new CountDownLatch(10);
然后每次执行 cont.countDown() 那计数器就会减一
Cont.await() 这个函数 如果cont里面减为0的话 就会执行者下面的语句 否则就不会执行。
只要cont为0 那么cont.await() 的那个线程就会加入争抢资源的队列中。
CyclicBarrier
一个操作等待所有的线程全部执行完毕 才执行下面的代码
CyclicBarrier cc = new CyclicBarrier(10);
创建线程的时候需要需要将这个cc传入进去 然后才可以有cc.await()
Cc.await(); //这后面的操作要等前面所有的线程都执行完毕。
Semaphore
availablePermits() 就是看现在还有多少资源 返回一个int类型的数据 每一个线程调用一次就会减1 然后每一个线程释放一次机会加1.
Acquire() 就是去请求资源 如果现在没有资源就会一直在哪里等待。
Release() 就行完毕之后就会释放资源。
并发队列
有界和无界的概念
有界就是有限制 无界就是没有限制。
阻塞队列和非阻塞队列的区别: 生产者在队列满的时候就被阻塞,消费者在队列空的时候就被阻塞。
队列 ConcurrentLinkedDeque 这是无界队列
Offer() 往队列里面添加元素 Poll() 取队头元素 size-- Peek() 取队头元素 size不变
BlockingQueue 阻塞队列 有界队列
线程池
线程池的四种创建方式
可缓存的线程池 newCachedThreadPool
package thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreaTest01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
for(int i=0; i<20; i++) {
int temp = i;
newCachedThreadPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Thread: "+Thread.currentThread().getName()+" i:"+temp);
}
});
}
}
}
结果:
Thread: pool-1-thread-1 i:0
Thread: pool-1-thread-2 i:1
Thread: pool-1-thread-3 i:2
Thread: pool-1-thread-4 i:3
Thread: pool-1-thread-2 i:16
Thread: pool-1-thread-4 i:18
Thread: pool-1-thread-1 i:15
Thread: pool-1-thread-3 i:14
Thread: pool-1-thread-7 i:6
Thread: pool-1-thread-8 i:7
Thread: pool-1-thread-6 i:5
Thread: pool-1-thread-9 i:8
Thread: pool-1-thread-10 i:9
Thread: pool-1-thread-11 i:10
Thread: pool-1-thread-12 i:11
Thread: pool-1-thread-13 i:12
Thread: pool-1-thread-16 i:19
Thread: pool-1-thread-14 i:13
Thread: pool-1-thread-15 i:17
Thread: pool-1-thread-5 i:4
可定长度的线程池 newFixedThreadPool
就是可以固定线程的个数 比如规定线程的个数为3 如下:
代码:
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
for(int i=0; i<10; i++) {
int temp = i;
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Thread: "+Thread.currentThread().getName()+" i:"+temp);
}
});
}
运行结果:
Thread: pool-1-thread-3 i:2
Thread: pool-1-thread-1 i:0
Thread: pool-1-thread-2 i:1
Thread: pool-1-thread-1 i:4
Thread: pool-1-thread-3 i:3
Thread: pool-1-thread-3 i:7
Thread: pool-1-thread-3 i:8
Thread: pool-1-thread-3 i:9
Thread: pool-1-thread-1 i:6
Thread: pool-1-thread-2 i:5
可定时线程池 newScheduledThreadPool
就是可以设置线程什么时间之后开始启动
代码:
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(3);
for(int i=0; i<10; i++) {
int temp = i;
newScheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("Thread: "+Thread.currentThread().getName()+" i:"+temp);
}
}, 5, TimeUnit.SECONDS);
}
运行结果: 五秒之后开始启动的线程
Thread: pool-1-thread-2 i:2
Thread: pool-1-thread-1 i:0
Thread: pool-1-thread-3 i:1
Thread: pool-1-thread-2 i:3
Thread: pool-1-thread-1 i:4
Thread: pool-1-thread-3 i:5
Thread: pool-1-thread-1 i:7
Thread: pool-1-thread-3 i:8
Thread: pool-1-thread-2 i:6
Thread: pool-1-thread-1 i:9
单线程 newSingleThreadScheduledExecutor
代码:
ScheduledExecutorService newSingleThreadExecutor = Executors.newSingleThreadScheduledExecutor();
for(int i=0; i<10; i++) {
int temp = i;
newSingleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Thread: "+Thread.currentThread().getName()+" i:"+temp);
}
});
}
结果:
Thread: pool-1-thread-1 i:0
Thread: pool-1-thread-1 i:1
Thread: pool-1-thread-1 i:2
Thread: pool-1-thread-1 i:3
Thread: pool-1-thread-1 i:4
Thread: pool-1-thread-1 i:5
Thread: pool-1-thread-1 i:6
Thread: pool-1-thread-1 i:7
Thread: pool-1-thread-1 i:8
Thread: pool-1-thread-1 i:9
线程池最后要停止需要调用shutdown();
线程池的原理分析
Cpu密集IO密集