多线程
并发
并行
多线程实现方式
1.继承Thread类
自己创建一个类extends thread类
Start方法开启线程,自动执行重写之后的run方法
2.实现runable接口
自己创建一个类implements runnable
Myrun不能直接使用getname方法,因为这个方法是thread类的方法
所以需要得到当前线程的thread对象,再用getname方法
3.用callable接口和future接口方法
抽象方法run没有返回值,获取不了运行结果
实现callable接口
重写call方法
Future接口不能直接用,要用他的实现类futuretask管理线程运行结果
Thread的方法
Getname
没有起名,有默认名字:Thread-0,Thread-1.。。
Setname
设置线程名字,可以用set,也可以用构造方法,但是构造方法不能继承,要自己重新写个构造方法,通过super继承父类的构造方法
CurrentThread
Sleep 静态方法 用Thread.sleep调用
抢占式调度
随机
优先级最高10,最小1,默认5
Setpriority(int)
Getpriority
守护线程
SetDaemon(true)
非守护和守护默认没有优先级之分
非守护结束,守护慢慢就也会结束
出让线程
Thread.yield()
让结果每一个线程尽可能均匀运行
插入线程
线程的生命周期
有执行资格:有资格去抢CPU的执行权
没有执行权:还没抢到执行权,不能执行代码
线程安全
Ticket要用static修饰,这样所有这个类的对象就可以共享ticket
为什么会出现这个问题?
执行代码时,线程随时都会被其他线程抢夺执行权
如何解决?
同步代码块
让一个线程执行完完整的一次同步代码块里的代码,才可以重新抢夺执行权
Synchronized(锁对象){}
锁对象:是任意的,但是一定是唯一的,前面用static修饰
锁对象可以是本类的字节码文件
同步方法
如果想把一个方法里面所有的代码都锁起来,就不需要同步代码块
直接将synchronized加在方法上
用runable的时候,由于只需要创建一次,所以里面的ticket就可以不用static修饰
Stringbuffer用于多线程环境,里面的方法和stringbuilder一样的
Lock锁
是一个接口,创建对象要用实现类reentrantlock
以上代码可能出现的问题:
- 如果lock没有用static修饰
则会所有的线程都会创建一个lock,重复ticket,超出范围的问题又会出现,所以要加static修饰,即所有线程共享一个lock,就能解决
2.程序不停止?
没有执行lock.unlock()方法,有线程一直停留在lock.lock()方法,所以程序没有停止
如何避免?
把unlock写到finally中,保证unlock一定会执行
死锁
不要让两个锁嵌套起来写,这样程序就运行不下去
生产者和消费者
等待唤醒机制
生产者:生产数据
消费者:消费数据
消费者等待
生产者等待
完整机制
方法
Wait
一般用notifyall
这几个方法要通过锁对象来进行调用
重写run方法的套路
Desk
Foodie
Cookie
测试类
第二种实现方式:阻塞队列方式
- 数组,有界
- 链表,无界
生产者和消费者要使用同一个阻塞队列
不需要在写锁对象,因为put和take方法的底层就已经有锁了
Take有返回值,类型和put进去的数据一样
线程的状态
Java没有定义运行状态,只有剩下的六种状态
为什么没有?
因为线程抢到CPU的执行权之后,当前线程就会交给操作系统管理,虚拟机就不会再管这个线程了
package exericise;
public class exercise1 {
public static void main(String[] args) {
window t1 = new window();
window t2 = new window();
t1.setName("窗口1");
t2.setName("窗口2");
t1.start();
t2.start();
}
}
package exericise;
public class window extends Thread{
static int ticket =1000;
@Override
public void run() {
while (true) {
synchronized (exercise1.class){
if (ticket != 0){
ticket--;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(getName() + "还有"+ticket+"张票");
}else {
break;
}
}
}
}
}
package exericise;
public class exercise2 {
public static void main(String[] args) {
Person t1 = new Person();
Person t2 = new Person();
t1.setName("Person1");
t2.setName("Person2");
t1.start();
t2.start();
}
}
package exericise;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Person extends Thread {
static int gift = 1000;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (!(gift < 10)) {
System.out.println(getName() + "送出第" + gift + "个礼物");
gift--;
} else {
break;
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
}
package exericise;
public class exercise3 {
public static void main(String[] args) {
Number t1 = new Number();
Number t2 = new Number();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
package exericise;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Number extends Thread{
static int start = 1;
static int end = 100;
static Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (start <= end) {
//判断是不是奇数
if (start %2 == 1) {
//是就打印
System.out.println(getName() + ":" + start);
}
start++;
}else {
//超过范围
break;
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
}
package exericise;
import java.util.Arrays;
import java.util.Random;
public class exercise4 {
public static void main(String[] args) {
//随机分成3个红包
Random r = new Random();
for (int i = 0; i < redBao.arr.length; i++) {
if (i == redBao.arr.length - 1) {
redBao.arr[i] = redBao.redPocket;
}else {
redBao.arr[i] = r.nextDouble(0.01,redBao.redPocket);
redBao.redPocket -= redBao.arr[i];
}
}
System.out.println(Arrays.toString(redBao.arr));
redBao rb1 = new redBao();
redBao rb2 = new redBao();
redBao rb3 = new redBao();
redBao rb4 = new redBao();
redBao rb5 = new redBao();
rb1.setName("No.1");
rb2.setName("No.2");
rb3.setName("No.3");
rb4.setName("No.4");
rb5.setName("No.5");
rb1.start();
rb2.start();
rb3.start();
rb4.start();
rb5.start();
}
}
package exericise;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class redBao extends Thread{
static double redPocket = 100;
//标记是否已经抢到过
boolean flag = false;
static double[] arr = new double[3];
static Lock lock = new ReentrantLock();
static int index = 0;
static int end = 1;
@Override
public void run() {
lock.lock();
try {
if (!flag && index <= 2){
//还没抢到过红包
System.out.println(getName() + "抢到了" + arr[index] + "块钱");
index++;
flag = true;
}
if (end >3) {
System.out.println(getName() + "没抢到");
}
end++;
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
在run方法中定义一个集合,则所有的进程都会在自己的栈中的run方法里创建出自己的集合,这个集合在不同的进程中独立,互不影响
注意:其实每一个线程都会有自己独立的栈,包括main,里面创建的元素互不影响(除非是static)
线程池
创建线程浪费时间
用完直接丢弃,浪费资源
原理
一般不会关闭线程池:因为服务器24小时都会运行,所以随时都会新任务进来,所以一般不会关闭线程池
Submit:提交任务
Shutdown:销毁线程池
0
自定义线程池
什么时候创建临时线程?
核心线程已被占用,而且排队队伍已满,这是才创建临时线程
先提交的任务,不一定先执行
如图,任务4 5 6在排队,临时线程处理任务7 8
当提交的任务数量大于核心线程数+临时线程数+队伍长度,剩下的任务10就会触发任务拒绝策略
自定义线程池,设置七个参数
ThreadPoolExecutor
到底怎么设置才合适?
最大并行数
系统处理器的线程数,4核8线程,8就是最大并行数
CPU密集型运算:读取文件较少,计算数据较多
为什么+1:如果前面的线程出问题,则多出来的线程就可以顶上,不浪费cpu的时钟周期不被浪费
IO密集性运算:现在的项目大多都是IO密集性的,读取服务器文件操作多
怎么得到计算时间?
通过thread dump的工具来测试计算时间