线程创建三种方式
1、继承Thread类 线程不一定立即执行,CPU安排调度
2、实现Runnable接口 把Runnable接口的实现类丢进Thread里面,再调用start(代理)
3、实现Callable接口(了解) 1、创建执行服务 2、提交执行 3、获取结果 4、关闭服务
Callable 实现多线程好处:
1、可以定义返回值
2、可以抛出异常
/**
* 回顾总结线程的创建
*/
public class ThreadNew {
public static void main(String[] args) {
new MyThread1().start();
new Thread(new MyThread2()).start();
FutureTask<Integer> futureTask=new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
try {
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1、继承Thread类
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("MyThread1");
}
}
//2、实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("MyThread2");
}
}
//实现Callable接口
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("MyThread3");
return 100;
}
}
run()方法线程执行体,start()方法启动线程
继承Thread类 启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性
实现Runnable接口 启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
(类的单继承和接口的多继承)
静态代理的思想。
Thread代理真实的Runnable接口的实现。
/**
* 线程创建的方式1:继承Thread类 重写run()方法,调用start开启线程
* 注意:线程开启不一定立即执行,由cpu调度执行。
*/
public class CreatThread1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是run线程"+i);
}
}
public static void main(String[] args) {
//创建一个线程对象
CreatThread1 creatThread1=new CreatThread1();
//调用start()方法开启线程
creatThread1.start();
for (int i = 0; i < 20; i++) {
System.out.println("我是主线程"+i);
}
}
}
/**
* @author 超厉害的我啊
* @date 2021/3/11 14:36:42
*
* 创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢人runnable接口实现类,调用start方法
*/
public class CreatThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是run线程"+i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
CreatThread2 creatThread2=new CreatThread2();
//创建线程对象,通过线程对象来开启我们的线程,代理
// Thread thread=new Thread(creatThread2);
// thread.start();
new Thread(creatThread2).start();
for (int i = 0; i < 20; i++) {
System.out.println("我是主线程"+i);
}
}
/**
* Callable 实现多线程好处
* 1、可以定义返回值
* 2、可以抛出异常
*/
public class TestCallable implements Callable<Boolean> {
//网络图片地址
private String url;
//保存的文件名
private String name;
public TestCallable(String url,String name){
this.url=url;
this.name=name;
}
@Override
public Boolean call() {
WebDownloader2 webDownloader=new WebDownloader2();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1=new TestCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=1.jpg","0.jpg");
TestCallable t2=new TestCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=2.jpg","1.jpg");
TestCallable t3=new TestCallable("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=3.jpg","2.jpg");
//创建执行服务
ExecutorService ser= Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1=ser.submit(t1);
Future<Boolean> r2=ser.submit(t2);
Future<Boolean> r3=ser.submit(t3);
//获取结果
boolean rs1=r1.get();
boolean rs2=r2.get();
boolean rs3=r3.get();
//关闭服务
ser.shutdown();
}
}
class WebDownloader2{
//下载方法
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异常,downloader方法出现问题");
}
}
}
public class TestThread2 implements Runnable{
//网络图片地址
private String url;
//保存的文件名
private String name;
public TestThread2(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) {
TestThread2 t1=new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=0.jpg","0.jpg");
TestThread2 t2=new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=0.jpg","1.jpg");
TestThread2 t3=new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1642885033,236479185&fm=26&gp=0.jpg","2.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(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异常,downloader方法出现问题");
}
}
}
线程的五大状态:
1、创建状态 new出来的时候
2、就绪状态 调用start方法
3、阻塞状态 sleep、wait、同步锁定
4、运行状态
5、死亡状态 一旦进入死亡状态,线程就不能再次启动
线程停止:
1、建议线程正常停止---->利用次数,不建议死循环
2、建议使用标志位------>设置一个标志位
3、不要使用stop或者destroy等过时或者JDK不建议使用的方法
/**
* 测试stop
* 1、建议线程正常停止---->利用次数,不建议死循环
* 2、建议使用标志位------>设置一个标志位
* 3、不要使用stop或者destroy等过时或者JDK不建议使用的方法
*
*/
public class TestStop 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) {
TestStop testStop=new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==900){
//调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
线程休眠:
sleep(时间)指定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep时间达到后 线程进入就绪状态
sleep可以模拟网络延迟、倒计时等
每个对象都有一个锁,sleep不会释放锁
/**
* 模拟倒计时
*/
public class TestSleep2 {
public static void main(String[] args) {
//打印当前系统时间
Date startTime=new Date(System.currentTimeMillis());//获取系统当前时间
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime=new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//模拟倒计时
public static void tenDown() throws InterruptedException {
int num=10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
}
线程礼让(Yield)
让当前正在执行的线程停止,但不阻塞
将线程从运行状态转为就绪状态
让cpu重新调度,礼让不一定成功,看cpu心情
**
* 测试礼让线程
* 礼让不一定成功,看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(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
线程的优先级Priority
Java提供一个线程调度器来监控启动后进入就绪状态的所有线程
线程的优先级用数字表示,范围1-10 数字大的优先级高,优先级高只是意味着获得调度的概率低,真正被调用还是靠Cpu调度
守护(daemon)线程
线程分为用户线程(main)和守护线程(gc)
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如后台记录操作日志、监控内存、垃圾回收等待
/**
* 测试守护线程
* 神仙保佑你
*/
public class TestDaemon {
}
//神仙
class God implements Runnable{
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();
}
@Override
public void run() {
while (true){
System.out.println("神仙保佑者你");
}
}
}
//你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("每天都开心");
}
System.out.println("一直很开心");
}
}
线程不安全的三种例子:
车站买票
银行取钱
线程不安全的集合
线程同步:
同步方法:
synchronized方法,默认锁的是this(对象本身)
缺点:如果将一个大的方法声明为synchronized将会影响效率
synchronized块
synchronizen(Obj){}Obj的对象是变化的量,需要增删改的对象
死锁:(多个线程互相抱着对方需要的资源,然后形成僵持)
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能
运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情
形,某一个同步块同时拥有两个以上对象的锁时,就可能发生死锁的问题
产生死锁的四个必要条件:
1、互斥条件:一个资源每次只能被一个进程使用
2、请求与保持条件:一个进程因请求而阻塞时,对已获得的资源保持不放
3、不可剥夺条件:进程已获得的资源,在未使用完之前,不难强行剥夺
4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
避免死锁的方法就是破坏死锁的四个必要条件之一
Lock(锁)
JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具
ReentrantLock(可重入锁)类实现了Lock
/**
* 测试Lock锁
*/
public class TestLock {
public static void main(String[] args) {
TestTicket2 testTicket2=new TestTicket2();
new Thread(testTicket2).start();
new Thread(testTicket2).start();
new Thread(testTicket2).start();
}
}
class TestTicket2 implements Runnable{
int ticketNums = 10;
//定义lock锁
private final ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if(ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else {
break;
}
} finally {
//解锁
lock.unlock();
}
}
}
}
Synchronized与Lock的对比
1、Lock是显示锁(手动开启和关闭锁,别忘记关闭锁)synchronizen是隐式锁,出了作用域自动释放
2、Lock只有代码块锁,synchronized有代码块锁和方法锁 3、使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供更多的子类)
4、优先使用顺序
Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
线程通信
方法:作用
wait():表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout):指定等待的毫秒数
notify():唤醒一个处于等待状态的线程 notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
线程池
JDK5提供线程池相关API:ExecutorService和Executors
ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor
void execute(Runnable) 执行任务/命令,没有返回值 一般用来执行Runnable
Futuresubmit 执行任务,有返回值,一般用来执行Callable
void shutdown() 关闭连接池
Executors 工具类,线程池的工厂类,用于创建并返回不同类型的线程池
/**
* 测试线程池
*
*/
public class TestPool {
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());
//关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}