多线程详解
一、线程是什么?
二、使用
1.创建线程的方式
1.1继承Thead类,重写run方法
package org.example.Thread;
//创建线程方式一:继承Thread类,重写run方法,调用start开启线程
//线程开启不一定立即执行,由CPU调度执行
public class ThreadTest01 extends Thread{
@Override
public void run(){
for (int i = 0;i<20;i++){
System.out.println("i am looking code--");
}
}
public static void main(String[] args) {
//创建线程对象
ThreadTest01 threadTest01=new ThreadTest01();
//开启线程
threadTest01.start();
for (int i = 0;i<200;i++){
System.out.println("i am learning thread---");
}
}
}
第一种方法的实例,使用commons.io包下载网络图片,需要传入图片的url地址
package org.example.Thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//创建线程方式一:继承Thread类,重写run方法,调用start开启线程
//线程开启不一定立即执行,由CPU调度执行
public class ThreadTest02 extends Thread{
private String url;
private String name;
public ThreadTest02(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownloader webDownloader=new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载文件名为:"+name);
}
public static void main(String[] args) {
ThreadTest02 t1=new ThreadTest02("https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=NBA&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=2932802967,3528389954&os=2432392431,2252291014&simid=4242616644,652387190&pn=1&rn=1&di=5500&ln=1214&fr=&fmq=1616121336734_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fn.sinaimg.cn%252Fsinacn20122%252F648%252Fw724h724%252F20190106%252Fc232-hrfcctn3149191.jpg%26refer%3Dhttp%253A%252F%252Fn.sinaimg.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1618713336%26t%3D5539acf22520b9c855e918c1b4674a70&rpstart=0&rpnum=0&adpicid=0&force=undefined&ctd=1616121338977^3_1905X929%1","1.jpg");
ThreadTest02 t2=new ThreadTest02("https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=NBA&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=2932802967,3528389954&os=2432392431,2252291014&simid=4242616644,652387190&pn=1&rn=1&di=5500&ln=1214&fr=&fmq=1616121336734_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fn.sinaimg.cn%252Fsinacn20122%252F648%252Fw724h724%252F20190106%252Fc232-hrfcctn3149191.jpg%26refer%3Dhttp%253A%252F%252Fn.sinaimg.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1618713336%26t%3D5539acf22520b9c855e918c1b4674a70&rpstart=0&rpnum=0&adpicid=0&force=undefined&ctd=1616121338977^3_1905X929%1","2.jpg");
ThreadTest02 t3=new ThreadTest02("https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=NBA&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined©right=undefined&cs=2932802967,3528389954&os=2432392431,2252291014&simid=4242616644,652387190&pn=1&rn=1&di=5500&ln=1214&fr=&fmq=1616121336734_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fn.sinaimg.cn%252Fsinacn20122%252F648%252Fw724h724%252F20190106%252Fc232-hrfcctn3149191.jpg%26refer%3Dhttp%253A%252F%252Fn.sinaimg.cn%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1618713336%26t%3D5539acf22520b9c855e918c1b4674a70&rpstart=0&rpnum=0&adpicid=0&force=undefined&ctd=1616121338977^3_1905X929%1","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
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("IO异常");
}
}
}
1.2实现Runnable接口
package org.example.Thread;
//创建线程方式二:实现runnable接口,重写run方法,执行线程需要丢入runnable的结构实现类,调用start开启线程
//线程开启不一定立即执行,由CPU调度执行
public class ThreadTest03 implements Runnable{
@Override
public void run() {
for (int i = 0;i<20;i++){
System.out.println("i am looking code--");
}
}
public static void main(String[] args) {
ThreadTest03 threadTest03=new ThreadTest03();
Thread thread=new Thread(threadTest03);
thread.start();
for (int i = 0;i<200;i++){
System.out.println("i am learning thread---");
}
}
}
第二种方法的实例,实现龟兔赛跑,兔子线程使用sleep()方法模拟休息
package org.example.Thread;
//模拟龟兔赛跑
public class Race implements Runnable{
private String winner;
@Override
public void run() {
for(int i=1;i<=100;i++){
if(Thread.currentThread().getName().equals("兔子")&&i%10==0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boolean flag=gameover(i);
if(flag) break;
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
public boolean gameover(int steps){
if(winner!=null) {
return true;
}
if(steps>=100){
winner=Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
两种方式的优缺点:
2.并发问题
模拟多个人买票
package org.example.Thread;
//发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱 这就是并发问题
public class ThreadTest04 implements Runnable{
private int ticketNums= 10;
@Override
public void run(){
while (true)
{
if (ticketNums<=0) break;;
System.out.println(Thread.currentThread().getName()+"-->拿到第 "+ticketNums--+" 票");
}
}
public static void main(String[] args) {
ThreadTest04 t=new ThreadTest04();
new Thread(t,"小明").start();
new Thread(t,"老师").start();
new Thread(t,"黄牛党").start();
}
}
多个线程操作同一资源的情况下,线程不安全,数据紊乱,这就是并发问题
三、线程的常用方法
3.1测试停止线程
1.建议线程正常停止–>利用次数,不建议死循环
2.建议使用标志位–>设置一个标志位
3.不要使用stop或者destory方法,官方已经推荐不使用
package org.example.Thread;
//测试停止线程
//1.建议线程正常停止-->利用次数,不建议死循环
//2.建议使用标志位-->设置一个标志位
//3.不要使用stop或者destory方法
public class ThreadTest05 implements Runnable{
private boolean flag=true;
@Override
public void run(){
int i=0;
while (flag)
{
System.out.println("run+"+i++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
ThreadTest05 t=new ThreadTest05();
new Thread(t).start();
for(int i=0;i<1000;i++){
System.out.println("main"+i);
if(i==900){
t.stop();
System.out.println("stop the thread");
}
}
}
}
3.2测试礼让线程
package org.example.Thread;
//测试礼让线程,礼让不一定成功,看CPU心情
public class ThreadTest06 implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+" start");
Thread.yield();
System.out.println(Thread.currentThread().getName()+" end");
}
public static void main(String[] args) {
ThreadTest06 t=new ThreadTest06();
new Thread(t,"a").start();
new Thread(t,"b").start();
}
}
测试礼让线程,礼让不一定成功,看CPU心情,结果不一样
礼让成功
礼让不成功
3.3测试join方法
join方法,想像为插队
package org.example.Thread;
//测试join方法,想像为插队
public class ThreadTest07 implements Runnable{
@Override
public void run(){
for (int i=0;i<1000;i++){
System.out.println("线程VIP来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadTest07 t=new ThreadTest07();
Thread thread = new Thread(t);
thread.start();
for (int i=0;i<500;i++){
if(i==200) thread.join();//插队
System.out.println("main "+i);
}
}
}
3.4测试线程的状态
package org.example.Thread;
//测试线程状态
public class ThreadTest08 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("/");
});//使用lambda表达式
Thread.State state=t.getState();
System.out.println(state);
t.start();
state=t.getState();
System.out.println(state);
while (state!=Thread.State.TERMINATED)
{
Thread.sleep(100);
state=t.getState();
System.out.println(state);
}
}
}
四.并发问题解决
4.1线程同步
用synchronized 改写买票例子,这个例子 synchronized用来修饰方法
package org.example.Thread.syn;
import org.example.Thread.ThreadTest04;
//不安全
public class BuyTicket implements Runnable{
private int ticketNums= 10;
private boolean flag=true;
@Override
public void run() {
while (flag) {
buy();
}
}
public synchronized void buy(){
if (ticketNums <= 0) {
flag=false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->拿到第 " + ticketNums-- + " 票");
return;
}
public static void main(String[] args) {
BuyTicket t=new BuyTicket();
new Thread(t,"小明").start();
new Thread(t,"老师").start();
new Thread(t,"黄牛党").start();
}
}
数据同步
银行取钱例子,两个人取同一个账户,同样用 synchronized,而这个例子锁住对象,需要增删改的对象
package org.example.Thread.syn;
//不安全,两个人去银行取钱,账户
public class UnsafeBank {
public static void main(String[] args) {
Account account=new Account(100,"老婆本");
Drawing drawing=new Drawing(account,50,"你");
Drawing drawing1=new Drawing(account,100,"她");
drawing.start();
drawing1.start();
}
}
//账户
class Account{
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
class Drawing extends Thread{
Account account;//账户
int drawingMoney;//取了多少钱
int nowMoney;//现在手里有多少钱
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱,synchronized 默认锁的是this
@Override
public void run() {
//锁对象的是变化的量,需要增删改
synchronized (account){
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money-=drawingMoney;
nowMoney+=drawingMoney;
System.out.println("余额为:"+account.money);
System.out.println(this.getName()+"手里的钱"+nowMoney);
}
}
}
4.2lock
同样是刚才例子 用lock实现
package org.example.Thread.lock;
import org.example.Thread.ThreadTest04;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest1 {
public static void main(String[] args) {
Test t=new Test();
new Thread(t,"老师").start();
new Thread(t,"小明").start();
new Thread(t,"黄牛党").start();
}
}
class Test implements Runnable{
private int ticketNums= 10;
//定义lock锁
private final ReentrantLock lock=new ReentrantLock();
@Override
public void run(){
while (true)
{ try {
lock.lock();
if (ticketNums<=0) break;;
System.out.println(Thread.currentThread().getName()+"-->拿到第 "+ticketNums--+" 票");
}finally {
lock.unlock();
}
}
}
}
4.3死锁
死锁的例子
package org.example.Thread.lock;
//死锁
public class DeadLock {
public static void main(String[] args) {
Makeup g1=new Makeup(0,"beatuiful girl");
Makeup g2=new Makeup(1,"ugly girl");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread {
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//名字
public Makeup(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//化妆,互相持有对方的资源,就是需要拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
Thread.sleep(1000);//一秒钟后获得镜子
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
}
}
}
else {
synchronized (mirror) {
System.out.println(this.girlName + "获得镜子的锁");
Thread.sleep(1000); //一秒钟后获得口红
synchronized (lipstick) {
System.out.println(this.girlName + "获得口红的锁");
}
}
}
}
}
4.3生产者消费者模式
4.3.1管程法
生产者消费者模型–>利用缓冲区解决:管程法
生产着,消费者,产品,缓冲区
package org.example.Thread.lock;
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer=new SynContainer();
Productor productor=new Productor(synContainer);
Consumer consumer=new Consumer(synContainer);
productor.start();
consumer.start();
}
}
//生产者
class Productor extends Thread{
SynContainer synContainer;
public Productor(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synContainer.push(new Chicken(i));
System.out.println("生产了第"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer synContainer;
public Consumer(SynContainer synContainer) {
this.synContainer = synContainer;
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了第"+ synContainer.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id){
this.id=id;
}
}
//缓冲区
class SynContainer{
//需要一个容器大小
Chicken[] chickens=new Chicken[10];
int count=0;
//生产者放入产品
public synchronized void push(Chicken chicken)
{
if(count== chickens.length)
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count++]=chicken;
this.notify();
}
//消费者消费产品
public synchronized Chicken pop(){
if(count==0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken=chickens[count];
this.notify();
return chicken;
}
}
4.3.2信号灯法
package org.example.Thread.lock;
//信号灯法
public class TestPC1 {
public static void main(String[] args) {
NBA nba=new NBA();
new Player(nba).start();
new Watcher(nba).start();
}
}
class Player extends Thread{
NBA nba;
public Player(NBA nba) {
this.nba = nba;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0) this.nba.play("爵士");
else this.nba.play("湖人");
}
}
}
class Watcher extends Thread{
NBA nba;
public Watcher(NBA nba) {
this.nba = nba;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.nba.watch();
}
}
}
class NBA{
String name;
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.name=name;
this.flag=!this.flag;
}
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了"+name+"的比赛");
this.notifyAll();
this.flag=!this.flag;
}
}
4.4线程池
package org.example.Thread.lock;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小
ExecutorService executorService= Executors.newFixedThreadPool(10);
executorService.execute(new MyThred());
executorService.execute(new MyThred());
executorService.execute(new MyThred());
executorService.execute(new MyThred());
//2.关闭连接
executorService.shutdown();
}
}
class MyThred implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
五.Lambda表达式
定义函数式接口:任何接口只包含一个抽象方法
package org.example.Lambda;
public class LambdaTest1 {
//3.静态内部类
static class YouHot2 implements Hot{
@Override
public void lambda() {
System.out.println("i am hot2");
}
}
public static void main(String[] args) {
Hot youhot=new YouHot();
youhot.lambda();
youhot=new YouHot2();
youhot.lambda();
//4.局部内部类
class YouHot3 implements Hot{
@Override
public void lambda() {
System.out.println("i am hot3");
}
}
youhot=new YouHot3();
youhot.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
youhot=new Hot() {
@Override
public void lambda() {
System.out.println("i am hot4");
}
};
youhot.lambda();
//用lambda简化
youhot=() ->{
System.out.println("i am hot5");
};
youhot.lambda();
}
}
//定义一个函数式接口:任何接口只包含一个抽象方法
interface Hot{
void lambda();
}
class YouHot implements Hot{
@Override
public void lambda() {
System.out.println("i am hot");
}
}
package org.example.Lambda;
public class Test2 {
public static void main(String[] args) {
Hot1 hot=(String name)->{
System.out.println(name+" is hot");
};
//简化参数类型
hot=(name)->{
System.out.println(name+" is hot");
};
//简化括号
hot=name->{
System.out.println(name+" is hot");
};
//简化花括号
hot=name->System.out.println(name+" is hot");
hot.lambda("jay");
}
}
interface Hot1{
void lambda(String name);
}