多线程
进程
一个内存中运行的应用程序,每个进程有一个独立的空间。
线程
- 每个线程都有自己的栈空间,共用一份堆内存
同步和异步
- 同步:排队执行,效率低但是安全
- 异步:同时执行,效率高但是不安全
- 并发:两个或者多个事件在同一时间段内发生
- 并行:两个或多个事件在同一时刻发生
线程实现
方法
- getName()获取线程名
- Thread.currentThread().getName();
- sleep为静态方法
实现Thread
- 构造方法
- new Thread();
- new Thread(String name)
- new Thread(Runnable, String name)
package com.lyq.kaikeba.codeLearn.threadLearn;
public class MyThread extends Thread{
@Override
public void run() {
// 新的执行路径
// 通过start()启动线程
}
}
继承Runnable
package com.lyq.kaikeba.codeLearn.threadLearn;
public class MyRunnable implements Runnable{
@Override
public void run() {
}
}
测试MyThread和MyRunnable
// 测试
package com.lyq.kaikeba.codeLearn.threadLearn;
/**
* 多线程技术
* 实现Runnable 与 继承Thread相比有如下优势:
* 1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合线程执行相同任务的情况
* 2. 可以避免单继承带来的局限性
* 3. 任务本身与线程是分离的,提高了程序的健壮性
* 4. 后续学习的线程池,接受Runnable类型的任务,不接受Thread类型的线程
*/
public class ThreadTest {
public static void main(String[] args) {
// 继承Thread用法
MyThread t1 = new MyThread();
t1.start();
// 实现Runnable用法
MyRunnable r1 = new MyRunnable();
Thread t = new Thread(r1);
t.start();
}
}
匿名内部类实现多线程
package com.lyq.kaikeba.codeLearn.threadLearn;
public class Demo1 {
public static void main(String[] args) {
// 匿名内部类实现多线程
new Thread(){
@Override
public void run() {
for(int i=0; i<10; i++){
System.out.println("thread " + i);
}
}
}.start();
for(int i=0; i<10; i++){
System.out.println("main " + i);
}
}
}
### 实现Runnable与继承Thread相比有如下优势:
1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合线程执行相同任务的情况
2. 可以避免单继承带来的局限性
3. 任务本身与线程是分离的,提高了程序的健壮性
4. 后续学习的线程池,接受Runnable类型的任务,不接受Thread类型的线程
线程中断
- 现在使用interrupt给线程打标记
- stop方法已经停用
package com.lyq.kaikeba.codeLearn.threadLearn;
public class Interrupt {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
for(int i=0; i<10; i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 给t1添加中断标记
t1.interrupt();
}
static class MyRunnable 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) {
// e.printStackTrace();
System.out.println("发现了中断标记,线程自杀");
return;
}
}
}
}
}
守护线程
- 线程分为守护线程和用户线程
- 用户线程:当一个线程不包含任何用户线程时,进程结束
- 守护线程:守护用户线程,当最后一个用户线程结束时,守护线程自动死亡
// 设置为守护线程
t1.setDaemon(true);
package com.lyq.kaikeba.codeLearn.threadLearn;
public class Interrupt {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
// 设置为守护线程
t1.setDaemon(true);
t1.start();
for(int i=0; i<10; i++){
System.out.println(Thread.currentThread().getName()+":"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 给t1添加中断标记
t1.interrupt();
}
static class MyRunnable 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) {
// e.printStackTrace();
System.out.println("发现了中断标记,线程自杀");
return;
}
}
}
}
}
线程同步问题
1、synchronized同步代码块(隐式锁)
- 对一个对象(任意对象)上锁,同一时间只有一个线程能访问代码块
package com.lyq.kaikeba.codeLearn.threadLearn;
/**
* 使用synchronized同步代码块
*/
public class Demo2 {
public static void main(String[] args) {
SellTicket s = new SellTicket();
new Thread(s).start();
new Thread(s).start();
new Thread(s).start();
}
static class SellTicket implements Runnable{
private int count = 10;
private Object o = new Object();
@Override
public void run() {
while(true){
synchronized (o){
if(count>0){
System.out.println("开始卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票为:"+count);
}else{
break;
}
}
}
}
}
}
2、synchronized同步方法(隐式锁)
- 对方法上锁
package com.lyq.kaikeba.codeLearn.threadLearn;
/**
* 使用synchronized同步方法
*/
public class Demo3 {
public static void main(String[] args) {
SellTicket s = new SellTicket();
new Thread(s).start();
new Thread(s).start();
new Thread(s).start();
}
static class SellTicket implements Runnable{
private int count = 10;
private Object o = new Object();
@Override
public void run() {
while(true){
boolean flag = sell();
if(!flag)
break;
}
}
public synchronized boolean sell(){
if(count>0){
System.out.println("开始卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票为:"+count);
return true;
}
return false;
}
}
}
3、显式锁:Lock
package com.lyq.kaikeba.codeLearn.threadLearn;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用Lock同步代码块
*/
public class Demo4 {
public static void main(String[] args) {
SellTicket s = new SellTicket();
new Thread(s).start();
new Thread(s).start();
new Thread(s).start();
}
static class SellTicket implements Runnable{
private int count = 10;
private Lock l = new ReentrantLock();
@Override
public void run() {
while(true){
l.lock();
if(count>0){
System.out.println("开始卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"出票成功,余票为:"+count);
}else{
break;
}
l.unlock();
}
}
}
}
公平锁和非公平锁
- 公平锁:排队抢占资源
- 非公平锁:抢占式抢资源
- 上述三种锁默认为非公平锁
- Lock可以使用公平锁:Lock l = new ReentrantLock(true);
线程死锁
消费者生产者问题
package com.java.demo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo4 {
/**
* 澶氱嚎绋嬮€氫俊闂, 鐢熶骇鑰呬笌娑堣垂鑰呴棶棰?
* @param args
*/
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<100;i++){
if(i%2==0){
f.setNameAndSaste("鑰佸共濡堝皬绫崇播","棣欒荆鍛?);
}else{
f.setNameAndSaste("鐓庨ゼ鏋滃瓙","鐢滆荆鍛?);
}
}
}
}
//鏈嶅姟鐢?
static class Waiter extends Thread{
private Food f;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i=0;i<100;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
f.get();
}
}
}
//椋熺墿
static class Food{
private String name;
private String taste;
//true 琛ㄧず鍙互鐢熶骇
private boolean flag = true;
public synchronized void setNameAndSaste(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 synchronized void get(){
if(!flag) {
System.out.println("鏈嶅姟鍛樼璧扮殑鑿滅殑鍚嶇О鏄?" + name + ",鍛抽亾:" + taste);
flag = true;
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
第三种实现多线程方法:Callable
package com.lyq.kaikeba.codeLearn.threadLearn;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo5 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Integer> c = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(c);
new Thread(task).start();
// 使用get方法后主线程会等子线程执行完再执行
Integer j = task.get();
System.out.println("返回值为:"+j);
Thread.sleep(100);
for(int i=0;i<10;i++){
System.out.println(i);
}
}
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread.sleep(100);
for(int i=0;i<10;i++){
System.out.println(i);
}
return 100;
}
}
}
线程池
频繁地创建和销毁线程需要时间,线程池可以节省这部分时间。
- 缓存线程池
长度无限制,
执行流程:
1. 判断线程池是否存在空闲
2. 存在则使用
3. 不存在,则创建线程并放入线程池,然后使用
package com.lyq.kaikeba.codeLearn.threadLearn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo6 {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
// 向线程池中添加任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
}
});
Thread.sleep(1000);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
}
});
}
}
- 定长线程池
长度指定
执行流程:
1. 判断线程池是否满了
2. 存在则使用
3. 不存在空闲线程,线程池未满则创建线程,并放入线程池,然后使用
4. 不存在空闲线程,线程池满了,则等待空闲线程
package com.lyq.kaikeba.codeLearn.threadLearn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 定长线程池
*/
public class Demo7 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
- 单线程池
线程空闲则使用,不空闲则等待
package com.lyq.kaikeba.codeLearn.threadLearn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo8 {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"test");
}
});
}
}
- 周期性任务定长线程池
在定长线程池的基础上,定时执行,当某个时机触发时,自动执行某任务
package com.lyq.kaikeba.codeLearn.threadLearn;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Demo9 {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 定时执行一次
* 参数1 : 定时执行的任务
* 餐数2 : 延迟时间
* 参数3 : 时间单位, 通过TimeUnit指定常量
*/
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("test");
}
}, 5, TimeUnit.SECONDS);
/**
* 周期性执行
* 参数1: 任务
* 参数2: 延迟执行时间(第一次多久之后执行)
* 参数3: 执行间隔时间
* 参数4: 时间单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("test1");
}
},5,1,TimeUnit.SECONDS);
}
}
Lambda表达式
package com.lyq.kaikeba.codeLearn.threadLearn;
/**
* Lambda表达式
*/
public class Demo10 {
public static void main(String[] args) {
new Thread(()-> System.out.println("Lambda表达式测试")).start();
}
}