概念:
进程:
线程:
线程调度:
分时调度:
抢占式调度:
同步与异步:
同步:
异步:
并发与并行:
并发:
并行:
public class MyThread extends Thread {
//run方法就是线程要执行的任务方法
@Override
public void run() {
//这里的代码就是一条新的执行路径
//这个执行路径处罚方法不是通过调用run方法,而是通过调用Thread对象的start()方法启动,
for (int i = 0;i<10;i++){
System.out.println("A"+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
for (int i = 0;i<10;i++){
System.out.println("B"+i);
}
}
}
B0 B1 B2 B3 B4 B5 A0 B6 A1 B7 A2 B8 A3 B9 A4 A5 A6 A7 A8 A9
//1.创建一个任务对象
MyRunnable t1 = new MyRunnable();
//2.创建一个线程,并分配任务
Thread ts = new Thread(t1);
//3.执行线程
ts.start();
1.通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行任务的情况
2.可以避免单继承所带来的局限性
3.任务与线程本身是分离的,提高了程序的健壮性
4.后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程
序号 | 变量和类型 | 方法 | 描述 |
---|---|---|---|
1 | long | getId() | 返回此Thread的标识符 |
2 | String | getName() | 返回此线程的名称 |
3 | int | getPriority() | 返回此线程的优先级 |
4 | void | setPriority(int newPriority) | 更改此线程的优先级 |
5 | static void | sleep(long millis) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性 |
6 | static void | sleep(long millis, int nanos) | 导致当前正在执行的线程休眠(暂时停止执行)指定的毫秒数加上指定的纳秒数,具体取决于系统定时器和调度程序的精度和准确性 |
public class GetThreadName implements Runnable{
@Override
public void run() {
//获取线程名称
Thread.currentThread().getName();
}
}
for (int i = 0;i<10;i++){
System.out.println(i);
//休眠时间(单位:毫秒)
Thread.sleep(1000);
}
一个线程是一个独立的执行路径,他是否应该结束,应该有其自身决定
如果强制中断线程可能导致线程占用的资源得不到释放
//对线程进行标记,提示线程应该结束了
t1.interrupt();
static class Num implements Runnable {
@Override
public void run() {
for (int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//对线程进行结束处理,释放线程占用的资源
System.out.println("Stop");
return;
}
}
}
}
线程:分为用户线程和守护进程
用户进程:当一个进程不包含任何存活的用户进程时,进程结束
守护进程:守护用户进程当最后一个用户进程结束时,所有的守护线程自动死亡
//设置为守护线程
t1.setDaemon(true);
static class Ticket implements Runnable{
private int count = 10;
@Override
public void run() {
while (count>0){
System.out.println("开始售票:");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:"+count);
}
}
}
当票数下降到临界值(即为1)时,线程执行时可能出现时间片丢失的情况,这时其他线程就会执行代码,此时票数的值还是临界值,但实际上应该是小于临界值,这时就会引发线程不安全问题
synchronized给进程加一把锁,让进程执行的时候能排队执行
使用synchronized修饰方法,就能使方法在执行时排队执行
同步方法锁的是调用方法的对象,但是当用static修饰方法时,锁的对象就变成了 类名.class
显示锁 Lock 子类 ReentrantLock
公平锁就是所有的线程获得时间片的方式是先到先得
非公平锁就是线程获得时间片的方式是抢夺时间片
当创建显示锁时传入一个fair参数true就为公平锁
在一个已经产生锁的方法中调用另一个可能产生锁的方法就有可能产生死锁的可能
public class DeadlockDemo {
public static void main(String[] args) {
Culprit c = new Culprit();
Police p = new Police();
new MyThread(c,p).start();
c.say(p);
}
static class MyThread extends Thread{
private Culprit c;
private Police p;
public MyThread(Culprit culprit, Police police){
this.c = culprit;
this.p = police;
}
@Override
public void run() {
p.say(c);
}
}
static class Culprit{
public synchronized void say(Police p){
System.out.println("a");
}
public synchronized void fun(){
System.out.println("b");
}
}
static class Police{
public synchronized void say(Culprit c){
System.out.println("c");
}
public synchronized void fun(){
System.out.println("d");
}
}
}
序号 | 类型和变量 | 方法 | 描述 |
---|---|---|---|
1 | void | notify() | 唤醒正在此对象监视器上等待的单个进程 |
2 | void | notifyAll() | 唤醒正在此对象监视器的所有进程 |
3 | void | wait() | 导致当前线程等待它被唤醒,通常是 通知 或 中断 |
4 | void | wait(long timeoutMillis) | 导致当前线程等待它被唤醒,通常是 通知 或 中断 , 或者知道进过一定量的实时 |
5 | void | wait(long timeoutMillis, int nanos) | 导致当前线程等待它被唤醒,通常是 通知 或 中断 ,或者知道进过一定量的实时 |
public class ProducerConsumer {
public static void main(String[] args) {
Food f = new Food();
new Cook(f).start();
new Waiter(f).start();
}
static class Cook extends Thread{
private Food f;
public Cook(Food f){
this.f = f ;
}
@Override
public void run() {
for (int i = 0;i<10;i++){
if (i%2 == 0){
f.setNameAndTaste("煎饼果子","香辣");
}else {
f.setNameAndTaste("驴脸蛋子","奥利给");
}
}
}
}
static class Waiter extends Thread{
private Food f;
public Waiter(Food f){
this.f = f ;
}
@Override
public void run() {
for (int i = 0;i<10;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
static class Food {
static boolean flag = true;
private String name;
private String taste;
public void setNameAndTaste(String name, String taste){
if (flag) {
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.taste = taste;
flag = false;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void get(){
if (!flag) {
System.out.println("端走的菜是" + name + ",味道" + taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
无限期等待另一个线程执行特定操作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行最多指定等待时间的操作的线程处于此状态。
TERMINATED
已退出的线程处于此状态
接口定义
//Callable接口
public interface Callable {
V call() throws Exception;
}
//Runnable接口
public interface Runnable {
public abstract void run();
}
Callable使用步骤
编写类实现Callable接口 , 实现call方法
class XXX implements Callable {
@Override
public call() throws Exception {
return T;
}
}
创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask future = new FutureTask<>(callable);
通过Thread,启动线程
new Thread(future).start();
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。
降低资源消耗
提高响应速度
提高线程的可管理性
//创建缓存线程池
ExecutorService service = Executors.newCachedThreadPool();
//创建定长线程池
ExecutorService service = Executors.newFixedThreadPool(线程池长度);
//创建单线程线程池
ExecutorService service = Executors.newSingleThreadExecutor();
//创建周期性任务定长线程池
ScheduledExecutorService service = Executors.newScheduledThreadPool(线程池长度);
缓存线程池.
(长度无限制)
执行流程:
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
Sou s = new Sou();
//向线程池中添加任务
service.execute(s);
service.execute(s);
service.execute(s);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(s);
service.execute(s);
}
static class Sou implements Runnable{
static int count = 0;
Lock l = new ReentrantLock();
@Override
public void run() {
l.lock();
System.out.println(Thread.currentThread().getName()+"A"+count);
count++;
l.unlock();
}
}
pool-1-thread-2A0
pool-1-thread-1A1
pool-1-thread-3A2
pool-1-thread-3A3
pool-1-thread-3A4
定长线程池.
(长度是指定的数值)
执行流程
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
Sou s = new Sou();
service.execute(s);
service.execute(s);
service.execute(s);
service.execute(s);
service.execute(s);
service.execute(s);
}
static class Sou implements Runnable{
static int count = 0;
Lock l = new ReentrantLock();
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"A"+count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
pool-1-thread-1A0
pool-1-thread-2A0
pool-1-thread-1A2
pool-1-thread-2A2
pool-1-thread-1A3
pool-1-thread-2A4
效果与定长线程池 创建时传入数值1 效果一致.
单线程线程池.
执行流程:
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
Sou s = new Sou();
service.execute(s);
service.execute(s);
service.execute(s);
service.execute(s);
}
static class Sou implements Runnable{
static int count = 0;
Lock l = new ReentrantLock();
@Override
public void run() {
l.lock();
System.out.println(Thread.currentThread().getName()+"A"+count);
count++;
l.unlock();
}
}
pool-1-thread-1A0
pool-1-thread-1A1
pool-1-thread-1A2
pool-1-thread-1A3
public static void main(String[] args) {
周期任务 定长线程池.
执行流程:
周期性任务执行时:
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
SimpleDateFormat date = new SimpleDateFormat("HH:mm:ss:ms");
Sou s = new Sou();
/**
* 1.定时执行一次
* 参数1.执行的任务
* 参数2.执行的时长数字
* 参数3.时长数字的单位, TimeUnit的常量指定
*/
System.out.println(date.format(new Date()));
service.schedule(s,5,TimeUnit.SECONDS);
/**
*周期执行
* - 参数1. runnable类型的任务
* - 参数2. 时长数字(延迟执行的时长)
* - 参数3. 周期时长(每次执行的间隔时间)
* - 参数4. 时长数字的单位
*/
service.scheduleAtFixedRate(s,5,5,TimeUnit.SECONDS);
}
static class Sou implements Runnable{
SimpleDateFormat date = new SimpleDateFormat("HH:mm:ss:ms");
static int count = 0;
Lock l = new ReentrantLock();
@Override
public void run() {
l.lock();
System.out.println(date.format(new Date()));
System.out.println(Thread.currentThread().getName()+"A"+count);
count++;
l.unlock();
}
}
public static void main(String[] args) {
//Lambda表达式
//(参数) -> {方法体}
new Thread(() -> System.out.println("A")).start();
print((int x, int y) ->{
return x+y;
},100,200);
}
private static void print(MyNum m, int x, int y) {
int sum = m.sum(x,y);
System.out.println(sum);
}
static interface MyNum{
int sum(int x, int y);
}