本文内容为我学习【狂神说Java】多线程详解的笔记
多线程
普通方法与多线程的区别
Process和Thread
- 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
- 进程是执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位
- 通常一个进程中可以包含多个线程,一个进程中至少有一个线程。线程是CPU调度和执行的单位(mian()方法就是主线程。gc线程)
- ps:很多多线程是模拟出来的,真正的多线程是指由多个CPU,即多核
线程创建
方法一 继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用**start()**方法启动线程
//创建线程方法一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread01 extends Thread{
@Override
public void run() {
//run方法线程体
for(int i = 0; i < 20; i++){
System.out.println("这是run方法第" +i+ "次");
}
}
public static void main(String[] args) {
TestThread01 testThread01 = new TestThread01();
testThread01.start();
//main线程
for(int i = 0; i < 200; i++){//次数多一点,方便观察
System.out.println("这是main方法第" +i+ "次");
}
}
}
方法二 实现Runnable(建议)
- 定义MyRunnable类实现Runnable接口(因为是接口,所以可以多继承;而且一个对象可以被多个线程使用(代理),这就是建议的原因)
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
public class TestRunnable02 implements Runnable{
@Override
public void run() {
//线程体
for (int i = 0; i < 100; i++) {
System.out.println("这里是run方法第" + i + "次");
}
}
public static void main(String[] args) {
//创建实现类对象
TestRunnable02 testRunnable = new TestRunnable02();
//创建代理类对象(new Thread(testRunnable).start();也可), 代理
Thread thread = new Thread(testRunnable);
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("这里是main方法第" + i + "次");
}
}
}
方法三 实现Callable接口(了解)
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable03 implements Callable<Boolean> {
private String url;
private String name;
@Override
public Boolean call() throws Exception {
WebDownloader we1 = new WebDownloader();
we1.downLoader(url,name);
System.out.println("文件" + name + "下载成功");
return true;
}
public TestCallable03(String url, String name){
this.name = name;
this.url = url;
}
public static void main(String[] args) {
TestCallable03 t = new TestCallable03("https://mmbiz.qpic.cn/mmbiz_png/eicUicStt1WicPcGIB5a6u4TeHjZbP5fkib543QQSFa5ku4KD1O4cZ3A9vsvGgNeVcNCXf61oksZGgKbuGvhtHyxwQ/0?wx_fmt=png","callable.png");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> result = ser.submit(t);
//获取结果
try {
boolean newResult = result.get();//不知道为什么这句话会有异常,所以我用了try-catch,结果没问题
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//关闭服务
ser.shutdownNow();
}
}
class webDownloader{
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("downLoader出现异常");
}
}
}
静态代理
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色(所以要传参)
好处:
代理对象可以作真实对象做不了的事
真实对象专注做自己的事
public class StaticProxy04 {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());//将真实角色你实例化代入代理角色
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实结婚角色-->你
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("peach");
}
}
//婚庆公司代理角色来帮助你
class WeddingCompany implements Marry{
//代理对象
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
beforeWedding();
this.target.HappyMarry();
afterWedding();
}
private void beforeWedding(){
System.out.println("结婚前");
}
private void afterWedding(){
System.out.println("结婚后");
}
}
Lambda表达式
- 避免匿名内部类定义过多
- 实质属于函数式编程的概念
//推导Lambda表达式
public class TestLambda05 {
//3.静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("lambda静态内部类");
}
}
public static void main(String[] args) {
//1、2
ILike like = new Like();//父类的引用指向子类,由于子类重写父类的方法,所以执行子类的方法
like.lambda();
//3.
like = new Like2();
like.lambda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("lambda局部内部类");
}
}
//4
like = new Like3();
like.lambda();
//5.匿名内部类(没有类的名称,必须借助接口或父类)
like = new ILike(){
@Override
public void lambda() {
System.out.println("lambda匿名内部类");
}
};
like.lambda();
//6.用lambda简化(删除5中new和lambda之间的单词,再加上->)
like = ()->{System.out.println("lambda简化");};
// ILike like2 = ()->{System.out.println("lambda");};//加上实例化对象一起
like.lambda();
//7.lambda还能简化
/*ILove love = null;
1.若方法只有一个参数,则可以再括号里直接写参数(不用写类型类型,参数类型由你调用该方法时用的参数类型确定)---love = (a)->{System.out.print("lambda");};
也可以不要括号love = a->{System.out.print("lambda");};
有多个参数,则不能去掉括号,但是也可以去掉参数类型--love = (a,b)->{System.out.print("lambda");};
2.若方法体只有一句话,则可以去掉花括号和一个分号---love = a->System.out.print("lambda");
*/
}
}
//1.先定义一个函数式接口(只有一个方法)
interface ILike{
void lambda();
}
//2.实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("lambda");
}
}
线程状态
线程停止
- 不推荐使用JDK提供的stop()\destroy()方法(已废弃)
- 推荐线程自己停止下来
- 建议使用一个标志位进行终止变量
public class TestStop06 implements Runnable{
//设置标志位
private Boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("This is run Thread " + (i++));
}
}
//设置一个公开的方法停止线程(转换标志位)
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop06 testStop = new TestStop06();
new Thread(testStop).start();
for (int i = 0; i < 100; i++) {
System.out.println("This is main Thread" + i);//???为什么main方法里如果没有内容循环输出,namrun方法了也不会输出内容
if (i==99){
//调用stop方法停止线程
testStop.stop();
System.out.println("线程停止");
}
}
}
}
线程休眠(Thread.sleep())
- 注意sleep存在异常,所以要自己捕获
//模拟倒计时
public class TestSleep2 {
//写成静态方法直接调用
public static void tenSecondsDown() throws InterruptedException {
int time = 10;
while(true){
Thread.sleep(1000);
System.out.println(time--);
if (time == 0){
break;
}
}
}
public static void main(String[] args) {
try {
tenSecondsDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程礼让(Thread.yield())
//礼让不一定成功,要看CPU调度
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println("MyYield-->" +Thread.currentThread().getName()+ "开始执行");
Thread.yield(); //礼让
System.out.println("MyYield-->" +Thread.currentThread().getName()+"停止执行");
}
}
线程强制执行(thread.join())
- Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞(想像成插队)
//注意不要使用我注释了的匿名初始化,这样会使join失败
//结果:先执行main方法至i=200,然后执行 完 所有的run方法,之后再执行剩余的main方法
//所有这样理解join:join语句在那个方法里,达到一定条件后,该方法就要让别的方法先走
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("run-->" + i);
}
}
public static void main(String[] args) {
TestJoin testJoin = new TestJoin();
// new Thread(testJoin).start();
Thread thread = new Thread(testJoin);
thread.start();
for (int i = 0; i < 500; i++) {
if(i == 200){
try {
// new Thread().join(); //注意这里要初始化
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main-->" + i);
}
}
}
线程状态的观测
//观察测试线程的状态
public class TestViewState {
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("/");
}
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);//结果:NEW (线程处于创建状态)
//启动线程
thread.start();
//更新线程状态
state = thread.getState();
System.out.println(state);//RUNNABLE (线程处于运行状态)
while(state != Thread.State.TERMINATED){//只要线程不终止,就一直输出
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新
state = thread.getState();//前面有sleep,所以此时为TIMED_WAITING
System.out.println(state);
}
}
}
线程的优先级
//测试线程的优先级-->一般优先级高(数字大)的会先走(但这是理想情况,现实下不一定:优先级高-->被CPU调用的 概率 高)
public class TestPriority {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+ "-->" +Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread th1 = new Thread(myPriority,"th1");
Thread th2 = new Thread(myPriority,"th2");
Thread th3 = new Thread(myPriority,"th3");
Thread th4 = new Thread(myPriority,"th4");
//先设置优先级,再启动
th1.start();
th2.setPriority(1);
th2.start();
th3.setPriority(4);
th3.start();
th4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY = 10
th4.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+ "-->" +Thread.currentThread().getPriority());
}
}
守护线程(thread.setDaemon(true))
//测试守护线程-->结果,用户线程结束后,守护线程跑了议会也停止了(JVM停止需要时间)
//上帝守护你
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
YouGuy youGuy = new YouGuy();
Thread thread = new Thread(god);
thread.setDaemon(true); //默认是false, 表示是用户线程.正常的线程都是用户线程
thread.start();//上帝守护线程启动
new Thread(youGuy).start();//你 用户线程 启动
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("God is here now and then.");
}
}
}
//你
class YouGuy implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("你活了 " +i+ " 天");
}
System.out.println("-------you died-----");
}
}
线程同步
- 多个线程操作同一资源
并发: 同一个对象被多个线程 同时操作(访问)
线程同步形成条件: 队列+锁
同步方法
同步块
- Obj是有增删改的对象
死锁
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup girl1 = new Makeup(0,"girl1");
Makeup girl2 = new Makeup(1,"girl2");
girl1.start();
girl2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
//化妆
class Makeup extends Thread{
//需要的资源只有一份,用static保证
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String name;//化妆的人
public Makeup(int choice, String name){
this.choice = choice;
this.name = name;
}
@Override
public void run() {
//化妆
makeup();
}
private void makeup(){
if(choice==0){
synchronized (lipstick){//获得口红的锁
System.out.println(this.name+ "获得口红的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (mirror){//1秒后想获得镜子的锁
System.out.println(this.name+ "获得镜子的锁");
}
}else{
synchronized (mirror){//获得镜子的锁
System.out.println(this.name+ "获得镜子的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lipstick){//1秒后想获得口红的锁
System.out.println(this.name+ "获得口红的锁");
}
}
}
}
- 上面的代码中,53行synchronized整个语句如果放在44行synchronized语句体里面, 66行synchronized整个语句如果放在57行synchronized语句体里面, 这样就会造成死锁
lock锁
import java.util.concurrent.locks.ReentrantLock;
//测试lock锁
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2,"Lock2 一").start();
new Thread(testLock2,"Lock2 二").start();
new Thread(testLock2,"Lock2 三").start();
}
}
class TestLock2 implements Runnable{
private int ticketNums = 10;
//定义lock锁
private final ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while(true){
try {//最外围加try-finally是为了给每个进入循环的线程加\解锁
reentrantLock.lock();//加锁
if (ticketNums>0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "拿到了第" +(ticketNums--)+ "张票");
}else{
System.out.println("票售尽");
break;
}
} finally {
reentrantLock.unlock();//解锁
}
}
}
}
- 由学过的同步方法知道,上面的代码中,也可以不加lock锁,直接在重写方法中加一个synchronized也能解决并发延时问题
线程协作
线程通信
解决方式一: 管程法
// 管程法 解决生产者消费者模型(利用缓存区)
//生产者\消费者\产品\缓冲区
public class SolutionPC01 {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
//生产者
class Producer extends Thread{
SynContainer container;
public Producer(SynContainer container){
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
container.push(new Production(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("消费了-->"+container.pop().id+"只鸡");
}
}
}
//产品
class Production{
int id; //产品编号
public Production(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//容器大小
Production[] pros = new Production[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Production pro){
//容器满了
if (count == pros.length) {
System.out.println("the synContainer is full");
try {
this.wait();//生产者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//容器未满-->丢入production
pros[count] = pro;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费production
public synchronized Production pop(){
//判断能否消费
if (count==0){
try {
this.wait();//消费者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Production pro = pros[count];
//通知生产者生产
this.notifyAll();
return pro;
}
}
解决方式二:
//信号灯法-->设置标志位解决
public class SolutionPC02 {
public static void main(String[] args) {
Show show = new Show();
new Actor(show).start();
new Audience(show).start();
}
}
//演员
class Actor extends Thread{
Show show;
public Actor(Show show){
this.show = show;
}
@Override
public void run() {
for (int i = 0; i <20 ; i++){
if(i%2==0) {
this.show.play("佚名");
}else{
this.show.play("广告");
}
}
}
}
//观众
class Audience extends Thread{
Show show;
public Audience(Show show){
this.show = show;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
show.watch(i);
}
}
}
//节目
class Show{
//演员表演,观众等待-->true
//观众观看,演员等待-->false
String program; //表演的节目
boolean flag = true;
//演员表演
public synchronized void play(String program){
if(!flag){
try { //观众观看,演员等待-->false
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了节目:" + program);
//通知观众观看
this.notifyAll();
this.program = program;
this.flag = !this.flag;
}
//观众看
public synchronized void watch(int i){
if(flag){//演员表演,观众等待-->true
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了节目:" + program);
System.out.println("------" + (i + 1) + "-----");
//通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class threadPool {
public static void main(String[] args) {
//1.创建服务,创建线程池(newFixedThreadPool()中参数为池子大小)
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}