java 多线程学习
java 多线程学习
跟随b站“遇见狂神说”学习,地址https://www.bilibili.com/video/BV1V4411p7EF
新手上路,才学疏浅,望斧正
1 基本概念
进程和线程
进程:最小的资源分配单位,一个程序就是一个进程。
线程:最小的运行单位,一个进程可将任务分配给多个线程执行。
并发和并行
并发:一个处理器处理多个任务。在宏观上看,多个任务是同时进行的,但是事实上多个任务是交替进行。
并行:多个处理器,每个处理器只处理一个任务。真正的同时进行。
生命周期,线程同进程一样,有这生命周期。
- 创建:一个新的线程被创建。
- 就绪:线程获得除cpu外所有资源,只要获得cpu即可执行。
- 运行:线程在cpu上运行。
- 阻塞:线程因等待资源或其他原因无法运行,等待条件满足后,恢复为就绪。阻塞并不是结束
- 死亡:线程执行完毕或者其他原因,导致线程结束。死亡后,就不能再次启动
2 进程创建三种方法
2.1 继承Thread
将一个类声明为Thread的子类。 这个子类应该重写run类的方法Thread 。
示例
//继承Thread 类
public class threadTest extends Thread{
//重写run方法
@Override
public void run() {
super.run();
for (int i =0; i < 1000; i++){
System.out.println("______"+i);
}
}
public static void main(String[] args) {
threadTest test=new threadTest();
//开启子线程
test.start();
for (int i = 0;i < 100; i++){
System.out.println("*****"+i);
}
}
}
开启子线程需要调用start()函数,而不是调用run() 函数。
应用:多线程下载图片(借助一个:org.apache.commons.io.FileUtils 包)
package com.demo1;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//多线程下载图片
public class PictDownload extends Thread {
String url;
String fileName;
public PictDownload( String url, String fileName) {
this.url = url;
this.fileName = fileName;
}
@Override
public void run() {
super.run();
try {
FileUtils.copyURLToFile(new URL(url),new File(fileName));
System.out.println(fileName+"has downloaded");
} catch (IOException e) {
e.printStackTrace();
System.out.println("图片下载错误");
}
}
public static void main(String[] args) {
PictDownload pictDownload1=new PictDownload("http://pic.bizhi360.com/bbpic/51/10551.jpg","001.jpg");
PictDownload pictDownload2=new PictDownload("http://pic.bizhi360.com/bbpic/50/10550.jpg","002.jpg");
PictDownload pictDownload3=new PictDownload("http://pic.bizhi360.com/bbpic/24/10524.jpg","003.jpg");
pictDownload1.start();
pictDownload2.start();
pictDownload3.start();
}
}
2.2 实现类Runnable
接口
另一种方法来创建一个线程是声明实现类Runnable
接口。 那个类然后实现了run
方法。 然后可以分配类的实例,在创建Thread
时作为参数传递,并启动。
示例
public class threadTest extends Thread{
@Override
public void run() {
super.run();
for (int i =0; i < 1000; i++){
System.out.println("______"+i);
}
}
public static void main(String[] args) {
threadTest test=new threadTest();
new Thread(test).start();
for (int i = 0;i < 100; i++){
System.out.println("*****"+i);
}
}
}
2.3 实现callable 接口
- 实现callable接口,重写call方法。
- 创建执行服务ExecutorService executorService=Executors.newFixedThreadPool(2);
- 提交服务Future
r1=executorService.submit(threadTest1); - 获取结果r1.get();
- 关闭服务executorService.shutdown();
package com.demo1;
import java.util.concurrent.*;
public class ThreadTest3 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i =0; i < 1000; i++){
System.out.println("______"+i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadTest3 threadTest1=new ThreadTest3();
ThreadTest3 threadTest2=new ThreadTest3();
ExecutorService executorService=Executors.newFixedThreadPool(2);
Future<Boolean> r1=executorService.submit(threadTest1);
Future<Boolean> r2=executorService.submit(threadTest2);
r1.get();
r2.get();
executorService.shutdown();
}
}
3 lamda 表达式
函数式接口:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
package com.demo2;
interface Test{
void testfun();
}
//外部类
class Test1 implements Test{
@Override
public void testfun() {
System.out.println("Test1");
}
}
public class LamdaTest {
//静态内部类
static class Test2 implements Test{
@Override
public void testfun() {
System.out.println("Test2");
}
}
public static void main(String[] args) {
//1使用外部类
Test1 test1=new Test1();
test1.testfun();
//2 使用静态内部类
Test2 test2=new Test2();
test2.testfun();
//3使用局部内部类
class Test3 implements Test{
@Override
public void testfun() {
System.out.println("Test3");
}
}
Test3 test3=new Test3();
test3.testfun();
//4 使用匿名内部类
Test test4 = new Test() {
@Override
public void testfun() {
System.out.println("Test4");
}
};
test4.testfun();
//5 使用lamda
Test test5=()->System.out.println("Test5");
test5.testfun();
}
}
4 线程控制
4.1 线程停止
注意事项
- 建议让进程正常停止,不建议使用死循环
- 建议使用标志位来控制线程停止
- 不使用官方不推荐的方法,如:stop(),destroy()等。
示例
package com.demo3;
public class TestStop implements Runnable {
//设置标志位
boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("子线程"+i++);
}
}
private void stop(){
flag=false;
}
public static void main(String[] args) {
TestStop testStop=new TestStop();
new Thread(testStop).start();
for (int i=0;i<1000;i++){
if(i==800)
testStop.stop();
System.out.println("main:"+i);
}
}
}
4.2 线程睡眠
线程睡眠即可令线程进入阻塞态,可以用于网络延时和计时器。
示例
package com.demo3;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
Date date=null;
while (true){
Thread.sleep(1000);
//获取系统当前时间
date=new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("hh:mm:ss").format(date));
}
}
}
4.3 线程礼让
礼让不一定成功,全看脸
示例
package com.demo3;
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束");
}
}
public class TestYield {
public static void main(String[] args) {
MyYield myYield=new MyYield();
new Thread(myYield).start();
new Thread(myYield).start();
}
}
4.4 线程插入
让cpu强制执行该线程
示例
package com.demo3;
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println("子:"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin=new TestJoin();
Thread thread=new Thread(testJoin);
thread.start();
for(int i=0;i<1000;i++){
if (i==500){
thread.join();
}
System.out.println("主:"+i);
}
}
}
5 线程同步和互斥
线程不安全示例
package com.demo4;
public class BuyTicket implements Runnable{
private boolean tag=true;
private int ticketNum=10;
private void buy(){
if(ticketNum<0){
tag=false;
return;
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while (tag){
buy();
}
}
public static void main(String[] args) {
BuyTicket buyTicket=new BuyTicket();
new Thread(buyTicket,"一号").start();
new Thread(buyTicket,"二号").start();
}
}
拿到了相同的票,这显然是不合理的。
synchronized
同步块:synchronized(obj){}
obj称为同步监视器,obj可以是任何对象,但是推荐使用共享资源作为同步监视器。
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身。
ReentrantLock lock 锁
显示的锁,手动开启和关闭。
while (true){
reentrantLock.lock();
try {
System.out.println(Thread.currentThread().getId()+"拿到了第"+ticketNum--);
}finally {
reentrantLock.unlock();
}
if(ticketNum>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
6 线程通信
利用缓存区:管程法解决生产者消费者问题
package com.demo5;
import java.util.Collection;
class Productor extends Thread{
TheBuffer theBuffer;
Productor(TheBuffer theBuffer){
this.theBuffer=theBuffer;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println("生产了"+i);
theBuffer.push(new Good(i));
}
}
}
class Comsumer extends Thread{
TheBuffer theBuffer;
Comsumer(TheBuffer theBuffer){
this.theBuffer=theBuffer;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
Good good=theBuffer.pop();
System.out.println("消费了第"+good.id);
}
}
}
class Good{
int id;
Good(int i){
this.id=i;
}
}
class TheBuffer{
Good[] goodList=new Good[10];
int count=0;
public synchronized void push(Good good){
if(count==9) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
goodList[count]=good;
count++;
this.notifyAll();
}
public synchronized Good pop(){
Good good=null;
if(count==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
good=goodList[count];
this.notifyAll();
return good;
}
}
public class TestPC {
public static void main(String[] args) {
TheBuffer theBuffer=new TheBuffer();
new Productor(theBuffer).start();
new Comsumer(theBuffer).start();
}
}
信号灯方法
package com.demo5;
import sun.security.krb5.internal.TGSRep;
class Player extends Thread {
TV tv;
Player(TV tv){
this.tv=tv;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 10; i++) {
tv.play(String.valueOf(i));
}
}
}
class Audience extends Thread{
TV tv;
Audience(TV tv){
this.tv=tv;
}
@Override
public void run() {
super.run();
for (int i = 0; i < 10; i++) {
tv.watch();
}
}
}
class TV{
boolean flag=true;
public synchronized void play(String name){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("表演了"+name);
this.notifyAll();
this.flag=!this.flag;
}
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了");
this.notifyAll();
this.flag=!this.flag;
}
}
public class TestPC2 {
public static void main(String[] args) {
TV tv=new TV();
new Player(tv).start();
new Audience(tv).start();
}
}
本文作者:发呆鱼
本文链接:https://www.cnblogs.com/dyiblog/articles/15782805.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步