Java线程学习
线程简介
-
任务
-
进程
-
线程
-
多线程
进程:是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位
通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位
注意:很多多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器;如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
线程实现
-
Thread class 继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
注意:线程开启不一定立即执行,由cpu调度执行
-
Runnable接口 实现Runnable接口
-
自定义线程类继承Runnable类
-
重写run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
区别
继承Thread类 实现Runnable接口 子类继承Thread类具备多线程能力 实现接口Runnable具有多线程能力 启动线程:子类对象.start() 启动线程:传入目标对象+Thread对象.start() 不建议使用,避免OOP单继承局限性 推荐使用,避免单继承局限性,灵活方便,方便同一个对象被多个线程使用 -
-
callable接口 实现callable接口(了解)
实现callable接口 需要返回值类型
重写call方法 需要抛出异常
//创建执行服务 ExecutorService ser= Executors.newFixedThreadPool(1); //提交执行 Future<Boolean> r1=ser.submit(t1); //获取结果 boolean rs1=r1.get(); //关闭服务 ser.shutdownNow(); -
静态代理
//真实对象和代理对象都要实现同一个接口 //代理对象要代理真实角色 //好处: //代理对象可以做很多真实对象做不了的事情 //真实对象专注做自己的事情 -
Lamda表达式
为什么使用lamda表达式:
避免匿名内部类定义过多
使代码更整洁
留下核心逻辑
new Thread (()->System.out.println("多线程学习。。。")).start();ILove love=(int a) ->{ System.out.println("i love you-->"+a); }; love.love(2);总结:只有一行则简化为一行,多行则用代码块
前提是接口为函数式接口
多个参数也可以去掉参数类型,要去得都去掉,且必须加括号
线程状态
-
new
Thread t=new Thread(),线程对象一旦创建就进入到了新生状态
-
就绪状态
当调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行
-
运行状态
进入运行状态,线程才真正执行线程体的代码块
-
阻塞状态
当调用sleep,wait或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待cpu调度执行
-
dead
线程中断或者结束,一旦进入死亡状态,就不能再次启动
方法 说明 setPriority(int newPriority) 更改线程的优先级 static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠 void join() 等待该线程终止 static void yield() 暂停当前正在执行的线程对象,并执行其他线程 void interrupt() 中断线程,不用这个方式 boolean isAlive() 测试线程是否处于活动状态
-
停止线程
不推荐使用jdk提供的stop(),destroy()方法,推荐线程自己停下来;使用一个标志位进行终止变量,当flag=false,则终止线程运行
public class TestStop implements Runnable{ //1.线程中定义线程体使用的标识 private boolean flag=true; @Override public void run(){ //2.线程体使用该标识 while(flag) } } -
线程休眠
指定当前线程阻塞的毫秒数;存在异常InterruptedException;时间达到后线程进入就绪状态;可以模拟网络延时,倒计时等;每一个对象都有一个锁,sleep不会释放锁;
Thread.sleep(1000); -
线程礼让
让当前正在执行的程序暂停,但不阻塞;让线程从运行状态转为就绪状态;让cpu重新调度,礼让不一定成功
Thread.yield();//礼让 -
Join
join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞;可以想象成插队
Thread.join();//插队 -
线程观察状态
Thread.State state=thread.getState(); -
线程优先级
线程优先级用数字表示,1~10
Thread.MIN_PRIORITY=1; Thread.MAX_PRIORITY=10; Thread.NORM_PRIORITY=5; //使用以下方式改变或获取优先级 getPriority.setPriority(int xxx)优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,都是看cpu的调度
-
守护线程(daemon)
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕,不用等待守护线程执行,如后台记录操作日志,监控内存
thread.setDaemon(true);//默认为false,表示用户线程,正常的线程都是用户线程
线程同步
-
并发:同一个对象被多个线程同时操作
-
为保证数据在方法中被访问时的正确性,在访问时加入锁机制 synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待
-
同步块方法及同步块
public synchronized void method(int args[]){}synchronized方法控制对‘对象’的访问,每个对象一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程阻塞;方法一旦执行,就独占该锁,直到该方法返回才释放,后面被阻塞的线程才能获得这个锁,继续执行
同步块:
synchronized(Obj){} //Obj称之为同步监视器,推荐使用共享资源作为同步监视器 -
死锁
某一个同步块同时拥有“两个以上对象的锁”时,就可能发生“死锁”问题。
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
-
Lock(锁)
ReentrantLock类实现了Lock,它拥有与synchronize相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁、释放锁。
synchronized与Lock对比:
lock是显式锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐式锁,出了作用域自动 释放。
lock只有代码块锁,synchronized有代码块锁和方法锁。
使用lock锁,jvm将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性
优先使用顺序:Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法 体之外)
线程通信问题
java提供了几个方法解决线程之间的通信问题
| 方法名 | 作用 |
|---|---|
| wait() | 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁 |
| wait(long timeout) | 指定等待的毫秒数 |
| notify() | 唤醒一个处于等待状态的线程 |
| notifyAll() | 唤醒同一个对象上所有调用wait方法的线程,优先级别高的线程优先调度 |
均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常
//练习线程,实现多线程同步下载
public class TestThead02 extends Thread{
private String url;
private String name;
public TestThead02(String url,String name){
this.name=name;
this.url=url;
}
//下载图片执行体
@Override
public void run() {
WebDownloader webDownloader=new WebDownloader();
webDownloader.download(url,name);
System.out.println("下载的文件名为:"+name);
}
public static void main(String[] args) {
TestThead02 t1=new TestThead02("图片地址","1.jpg");
TestThead02 t2=new TestThead02("图片地址","2.jpg");
TestThead02 t3=new TestThead02("图片地址","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器
class WebDownloader{
//下载方法
public void download(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("io异常,download方法出现问题");
}
}
}
/*结果:
下载的文件名为:3.jpg
下载的文件名为:1.jpg
下载的文件名为:2.jpg
执行顺序为123,但结果是按照网速为准,谁先下完就是谁
线程不一定执行,CPU安排调度
*/

浙公网安备 33010602011771号