线程创建
线程创建的三种方式:
1.Thread类,继承Threadlei
2.Runable接口,实现Runable接口
3.Callable接口,实现Callable接口
Thread类
Thread类实现线程:
1.自定义线程继承Thread类
2.重写run方法,编写线程执行体
3.创建线程对象,调用start()方法启动线程
代码展示:
调用start方法,main主线程和run方法线程同时进行,交叉执行
调用run方法,先执行run方法线程,再调用main主方法
多线程案例:网图下载
先下载commons io库
代码:
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
//练习Thread,实现线程同步下载图片
public class TestThread1 extends Thread {
private String url;
private String name;
@Override
public void run() {
WebDownloader webDownloader=new WebDownloader();
try {
webDownloader.downloader(url,name);
System.out.println("下载文件名为:"+name);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public TestThread1(String url, String name){
this.url=url;
this.name=name;
}
public static void main(String[] args) {
TestThread1 testThread1=new TestThread1("https://blog.kuangstudy.com/usr/them/handsome/usr/img/sj/1.jpg","2.jpg");
TestThread1 testThread2=new TestThread1("https://blog.kuangstudy.com/usr/them/handsome/usr/img/sj/2.jpg","3.jpg");
TestThread1 testThread3=new TestThread1("https://blog.kuangstudy.com/usr/them/handsome/usr/img/sj/3.jpg","4.jpg");
testThread1.start();
testThread2.start();
testThread3.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void downloader(String url,String name) throws IOException {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
}catch (IOException e){
e.printStackTrace();
System.out.println("IO异常");
}
}
}
Runnable接口
实现Runnable接口:
1.实现接口
2.启动线程:传入目标对象+Thread对象.start()
public class TextRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println("好好学习");
}
}
public static void main(String[] args) {
//创建Runnable接口实现的类对象
TextRunnable textRunnable=new TextRunnable();
//创建线程对象,通过线程对象来打开线程
new Thread(textRunnable).start();
for(int j=0;j<5;j++){
System.out.println("天天向上");
}
}
}
Runanble接口和Thread都可以实现线程,Thread可以直接实现开启线程,而Runnable接口要借助Thread传入参数开启。
Callable接口
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标
4.创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(1);
5.提交执行:Future<Boolean> result1=ser.submit(1)
6.获取结果:boolean r1=result1.get()
7.关闭服务:ser.shutsdownNow()
public class TextRunnable implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("好好学习");
return true;
}
public static void main(String[] args) throws Exception {
TextRunnable textRunnable=new TextRunnable();
TextRunnable textRunnable1=new TextRunnable();
// 4.创建执行服务:
ExecutorService ser= Executors.newFixedThreadPool(2); //创建两个线程池
//5.提交执行:
Future<Boolean> result1=ser.submit(textRunnable); //提交服务得到一个返回值
Future<Boolean> result2=ser.submit(textRunnable1);
//6.获取结果:
boolean r1=result1.get();
boolean r2=result2.get();
//7.关闭服务:
ser.shutdown(); //shutdownNow();
}
}
Lambda表达式
函数式接口的定义:
·任何接口,如果只包含唯一一个抽象方法,那么它就是函数接口
public interface Runnable{
public abstract void run();
}
·对应函数式接口,我们可以通过lamda表达式来创建接口的对象
public class Textlambda {
//3.静态内部类
static class like2 implements Ilike{
@Override
public void lambda() {
System.out.println("这是静态内部类方法");
}
}
public static void main(String[] args) {
Ilike like=new like();
like.lambda();//函数式接口
like=new like2();
like.lambda();//静态内部类
//4.局部内部类
class like3 implements Ilike{
@Override
public void lambda() {
System.out.println("这是局部内部类方法");
}
}
like=new like3();
like.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或父类
like=new Ilike() {
@Override
public void lambda() {
System.out.println("这是匿名内部类");
}
};
like.lambda();
//6.lamdba方法
like = ()->{
System.out.println("这是lambda方法");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface Ilike{
void lambda();
}
//2.实现类
class like implements Ilike{
@Override
public void lambda() {
System.out.println("这是lambda方法");
}
}
线程状态
线程方法:
方法 | 说明 |
setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程,别用这个方式 |
boolean isAlive() | 测试线程是否处于活动状态 |
线程停止:
//1.建议线程正常停止,利用次数,不建议用死循环
//2.建议用标志位
//3.不要使用stop或者destroy等过时或者JDK不建议使用的方法
public class Textlambda implements Runnable{
private boolean flag=true; //设置标识位
@Override
public void run() {
int i=0;
while(flag){
System.out.println("线程"+i++);
}
}
public void stop(){ //停止线程的方法
this.flag=false;
}
public static void main(String[] args) {
Textlambda textlambda=new Textlambda();
new Thread(textlambda).start();
for(int i=0;i<500;i++){
System.out.println("main"+i);
if(i==400){ //调用stop方法切换标志位
textlambda.stop();
System.out.println("线程停止");
}
}
}
}
线程休眠sleep
·sleep(时间)指定当前线程阻塞的毫秒数
·sleep存在异常InterrupttedException
·sleep时间达到后线程进入就绪状态
·sleep可以模拟网络延迟等
·每一个对象都有一个对象锁,sleep不会释放锁
public class Textlambda {
public static void main(String[] args) {
try {
testsleep();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void testsleep() throws InterruptedException {
int num=10;
while(true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
}
线程礼让yield
·礼让线程,让当前正在执行的线程暂停,但不阻塞
·将线程从运行状态转为就绪状态
·让CPU重新调度,礼让不一定成功
public class Textlambda {
public static void main(String[] args) {
yield yield=new yield();
new Thread(yield,"线程1").start();
new Thread(yield,"线程2").start();
}
}
class yield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
join
join合并线程,待次线程执行完成后,再执行其他线程,其他线程阻塞
public class Textlambda implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("线程1"+i);
}
}
public static void main(String[] args) throws InterruptedException {
Textlambda textlambda=new Textlambda();
Thread thread=new Thread(textlambda);
thread.start();
for (int i=0;i<200;i++){
System.out.println("main线程"+i);
if(i==50){
thread.join(); //线程礼让
}
}
}
}
线程状态观察Thread.State
·NEW 线程尚未启动
·RUNNABLE 线程在java虚拟机中执行
·BLOCKED 线程被阻塞等待监视器锁定
·WAITING 线程正在等待另一个线程执行待定动作
·TIMED_WAITING 线程正在等待另一个线程执行动作达到指定时间
·TERMINATED 线程退出
public class Textlambda {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
for(int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("线程就将停止");
});
//观察状态
Thread.State state=thread.getState();
System.out.println(state); //new
//观察启动后
thread.start();
state=thread.getState();
System.out.println(state); //Run
while (state!=Thread.State.TERMINATED){
Thread.sleep(1000);
state=thread.getState();//不断更新状态
System.out.println(state);
}
}
}
线程优先级
线程优先级,1~10
Thread.MIN_PRIORITY=1
Thread.MAX_PRIORITY=10
Thread.NORM_PRIORITY=5
改变优先级和获取优先级:getPriority() setPriority(int num)
public class Textlambda {
public static void main(String[] args) {
//主线程,默认优先级5
System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
mypriority mypriority=new mypriority();
Thread t1=new Thread(mypriority);
Thread t2=new Thread(mypriority);
Thread t3=new Thread(mypriority);
Thread t4=new Thread(mypriority);
t2.setPriority(2);
t3.setPriority(Thread.MIN_PRIORITY);
t4.setPriority(Thread.MAX_PRIORITY);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class mypriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
}
}
守护线程daemon
·线程分为用户线程和守护线程
·虚拟机必须保证用户线程执行完毕
·虚拟机不需要等待守护线程执行完毕
public class Textlambda {
public static void main(String[] args) {
God god=new God();
you you=new you();
Thread thread=new Thread(god);
thread.setDaemon(true);//默认为false表示用户线程
thread.start();
new Thread(you).start();//用户线程
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("保佑你活着");
}
}
}
//你
class you implements Runnable{
@Override
public void run() {
for(int i=0;i<3600;i++){
System.out.println("活着");
}
System.out.println("死亡");
}
}
线程同步
·由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放资源锁,但会产生如下问题:
1.一个线程持有锁会导致其他需要该锁的线程挂起
2.在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延迟,引起性能问题
3.如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置
同步方法:
·synchronized方法
public synchronized void method(int args){}
synchronized方法控制对象的访问,每一个对象对应一把锁,每个synchronized方法都必须获得调用该方法的锁才可以执行,否则线程会被阻塞,方法一旦执行,就独占该锁,直到该方法改回才释放锁,后面被阻塞的线程获得这个锁才可以执行。
public class Textlambda {
public static void main(String[] args) {
BuyTicket buyTicket=new BuyTicket();
new Thread(buyTicket,"消费者1").start();
new Thread(buyTicket,"消费者2").start();
new Thread(buyTicket,"黄牛").start();
}
}
class BuyTicket implements Runnable{
private int ticketNums=10; //票数
boolean flag=true; //外部停止方法
@Override
public void run() {
//买票
while(flag){
try {
buy();
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
//synchronixed方法
private synchronized void buy() throws InterruptedException {
if(ticketNums<=0){ //判断是否有票
flag=false;
return;
}
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
同步块
·synchronized块
synchroni(Obj){}
Obj称之为同步监视器,Obj可以是任何对象,但是推荐使用共享资源作为同步监视器,同步方法中无须指定同步监视器,因为同步方法的监视器就是this,就是这个对象本身。
同步监视器的执行过程:
1.第一个线程访问,锁定同步监视器,执行其代码
2.第二个线程访问,发现同步监视器被锁定,无法访问
3.第一个线程访问结束,解锁同步监视器
4.第二个线程访问,发现同步监视器没有锁,任何锁定并访问
死锁
产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保存条件:一个进程因请求资源而阻塞时,对已获得的资源保存不放。
3.不剥夺条件:进程获得的资源,在未使用完之前,不能强行剥夺
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
public class Textlambda {
public static void main(String[] args) {
Makeup makeup=new Makeup(0,"琳琳");
Makeup makeup1=new Makeup(1,"娜娜");
makeup.start();
makeup1.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread{
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;//选择
String girlname;
Makeup(int choice,String girlname){
this.choice=choice;
this.girlname=girlname;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//化妆方法,互相持有对方的锁,就是拿到对方的资源
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(2000);
synchronized (lipstick){ //一秒钟后获得口红
System.out.println(this.girlname+"获得口红的锁");
}
}
}
}}
认识,获得一个锁就得不到另一个锁
Lock锁
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的ReentrantLock,可以显式加锁,释放锁
public class Textlambda {
public static void main(String[] args) {
Textlock textlock=new Textlock();
new Thread(textlock,"1").start();
new Thread(textlock,"2").start();
new Thread(textlock,"3").start();
}
}
class Textlock implements Runnable{
int ticketnum=10;
//定义lock锁
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try{
lock.lock();//加锁
if (ticketnum > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketnum--);
} else {
break;
}
}finally {
lock.unlock();
}
}}}
synchronized与Lock的对比:
1.Lock是显式锁(需要手动开启和关闭锁),synchronized是隐式锁,出了作用域自动关闭
2.Lock只有代码块锁,synchronized有代码块锁和方法锁
3.优先使用:Lock>同步代码块>同步方法
线程通信:
线程通信方法:
方法名 | 作用 |
wait() | 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象使用调用wait()方法的线程,优先级高的线程优先调度 |
生产者消费者模式:(管程法)(信号灯法)
·生产者:负责产生数据
·消费者:负责处理数据
·缓冲区:消费者不能直接使用生产者的数据,他们之间有一个缓冲区
管程法:
public class Textlambda {
public static void main(String[] args) {
syncontainer container=new syncontainer();
new productor(container).start();
new consumber (container).start();
}
}
//生产者
class productor extends Thread{
syncontainer container;
public productor( syncontainer container){
this.container=container;
}
@Override
public void run() {
for(int i=0;i<100;i++){
container.push(new chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class consumber extends Thread{
syncontainer container;
public consumber(syncontainer container){
this.container=container;
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("消费了-->"+container.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) {
throw new RuntimeException(e);
}
}
//如果没有满。我们就需要丢入产品
chickens[count]=chicken;
count++;
//可以通知消费者消费了
this.notifyAll();
}
//消费者消费产品
public synchronized chicken pop(){
//判断能否消费
if(count==0){
try{
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//等待生产者生产,消费者等待
}
//如果可以消费
count--;
chicken chicken=chickens[count];
//吃完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
信号灯法:
public class Textlambda {
public static void main(String[] args) {
tv tv=new tv();
new player(tv).start();
new watcher(tv).start();
}
}
//生产者-->演员
class player extends Thread{
tv tv;
public player(tv tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%20==0){
this.tv.play("喜洋洋播放中");
}else {
this.tv.play("广告");
}
}
}
}
//消费者——>观众
class watcher extends Thread{
tv tv;
public watcher(tv tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品——>节目
class tv{
//演员表演,观众等待
//观众观看,演员等待
String voice;//表演的节目
boolean flag=true;
//表演
public synchronized void play(String voice){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("演员表演了:"+voice);
//通知观众观看
this.notifyAll();//通知唤醒
this.voice=voice;
this.flag=!this.flag;
}
//观看
public synchronized void watch() {
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("观看了:"+voice);
//通知演员表演
this.notifyAll();
this.flag=!this.flag;
}
}
线程池
背景:经常创建和销毁,使用量大的资源,对性能影响大
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完发回池中。
线程池API:ExecutorService和Executors
ExecutorServoce:真正到线程池接口
·void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
·void shutdown():关闭线程池
·<T>Future<T>submint(Callable<T>task):执行任务,有返回值,一般用来执行Callable
Executors:工具类,线程池的工厂类,用来创建并返回不同类型的线程池
public class Textlambda {
public static void main(String[] args) {
//1.创建服务,创建线程池,参数为线程池大小
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());
}
}
线程池。