多线程
使用继承Thread类开启多线程
例:
package thread;
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread01 extends Thread{
@Override
public void run() {
//run()方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我再看代码-------"+i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
TestThread01 testThread01 = new TestThread01();
//调用start()方法开启线程
testThread01.start();
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程----"+i);
}
//输出结果交替
}
}
输出结果:
我在学习多线程----0
我再看代码-------0
我在学习多线程----1
我再看代码-------1
我再看代码-------2
我再看代码-------3
我再看代码-------4
我再看代码-------5
我再看代码-------6
我再看代码-------7
我再看代码-------8
我再看代码-------9
我再看代码-------10
我再看代码-------11
我再看代码-------12
我再看代码-------13
我在学习多线程----2
我在学习多线程----3
我在学习多线程----4
我在学习多线程----5
我在学习多线程----6
我在学习多线程----7
我在学习多线程----8
我在学习多线程----9
我在学习多线程----10
我在学习多线程----11
我在学习多线程----12
我在学习多线程----13
我再看代码-------14
我在学习多线程----14
我再看代码-------15
我在学习多线程----15
我再看代码-------16
我在学习多线程----16
我在学习多线程----17
我在学习多线程----18
我在学习多线程----19
我再看代码-------17
我再看代码-------18
我再看代码-------19
进程已结束,退出代码0
- 总结:注意,线程开启不一定立刻执行,由cpu调度执行
- 步骤:
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
使用Runnable实现接口
-
例:
package thread; public class TestThread03 implements Runnable{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("我在看代码!====="+i); } } public static void main(String[] args) { TestThread03 testThread03 = new TestThread03(); // Thread thread = new Thread(testThread03); // thread.start(); new Thread(testThread03).start(); for (int i = 0; i < 20; i++) { System.out.println("我在学习双线程====="+i); } } }
小结
-
继承Thread类
- 子类继承Thread类具有多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
-
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动进程:传入对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
龟兔赛跑例子
package thread;
public class TestThread04 implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (Thread.currentThread().getName().equals("兔子")){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
boolean flag = gameOver(i);
if (flag==true){
break;
}
}
}
private boolean gameOver(int step){
if (step==100){
this.winner = Thread.currentThread().getName();
System.out.println("Winner is "+winner);
return true;
}
if (winner !=null){
return true;
}
return false;
}
public static void main(String[] args) {
TestThread04 testThread04 = new TestThread04();
new Thread(testThread04,"兔子").start();
new Thread(testThread04,"乌龟").start();
}
}
线程休眠sleep
-
sleep(时间)指定当前线程阻塞的毫秒数
-
sleep存在异常InterruptedException;
-
sleep事件达到后线程进入就绪状态
-
sleep可以模拟网络延时,倒计时等
-
例子:抢火车票
public class TestSleep implements Runnable{ private int ticketNum = 10; @Override public void run() { while(true){ if (ticketNum <= 0){ break; } try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNum--+"张票!"); } } public static void main(String[] args) { TestSleep ticket = new TestSleep(); new Thread(ticket,"小明").start(); new Thread(ticket,"黄牛").start(); new Thread(ticket,"旅游公司").start(); } }
线程礼让yield
-
礼让线程,让当前正在执行的线程暂停,但不堵塞
-
将线程从运行状态转为就绪状态
-
让CPU重新调度,礼让不一定成功
-
例子:
public class TestYield implements Runnable{ @Override public void run() { System.out.println("线程开始执行"+Thread.currentThread().getName()); Thread.yield(); System.out.println("线程停止执行"+Thread.currentThread().getName()); } public static void main(String[] args) { TestYield testyield = new TestYield(); new Thread(testyield,"a").start(); new Thread(testyield,"b").start(); } }
线程强制执行join
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("vip线程执行第"+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 < 100; i++) {
if (i==30){
thread.join();
}
System.out.println("主线程"+i);
}
}
}
线程优先级
-
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
-
线程的优先级用数字表示,范围1~10
- Thread.MIN_PRIORITY = 1
- Thread.MAX_PRIORITY = 10
- Thread.NORM_PRIORITY = 5
-
使用一下方式改变或获取优先级
- getPriority
- .setPriprity(int xxx)
-
优先级低只是意味着获得调度的概率低,并不是优先级第就不会被调用了。
-
优先级的设定建议在start()调度前
守护线程daemon
-
线程分为用户线程和守护线程
-
虚拟机必须确保用户线程执行完毕
-
虚拟机不用等待守护线程执行完毕
-
例子:上帝守护我们
public class TestDaemon { public static void main(String[] args) { You you = new You(); God god = new God(); Thread thread = new Thread(god); thread.setDaemon(true); thread.start(); new Thread(you).start(); } } class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("开心的活着!"); } System.out.println("拜拜了这个美丽的世界!!!!"); System.out.println("================================="); } } class God implements Runnable{ @Override public void run() { while(true){ System.out.println("上帝守护者你!"); } } }
同步方法synchronized
-
synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得该方法的对象的锁才能执行,否则相乘会堵塞,方法一旦执行,就独占该锁,值到该方法返回才释放锁,后面被堵塞的线程才能获得这个锁,继续执行
public synchronized void method(int args){}
或者使用synchronized块
synchronized(Obj){}
-
例:(银行取钱)改前
public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"张三"); Drawing you =new Drawing(account,50,"boyfriend"); Drawing girlfriend =new Drawing(account,100,"girlfriend"); you.start(); girlfriend.start(); } } //账户 class Account{ int money;//余额 String idname;//卡名 public Account(int money, String idname) { this.money = money; this.idname = idname; } } //银行 class Drawing extends Thread{ Account account; int drawingMoney; int nowMoney; public Drawing(Account account,int drawingMoney,String idname){ super(idname); this.account =account; this.drawingMoney = drawingMoney; } @Override public void run() { if(account.money - drawingMoney < 0){ System.out.println(Thread.currentThread().getName()+"钱不够,不能取!"); return; } try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } account.money = account.money - drawingMoney; nowMoney = nowMoney + drawingMoney; System.out.println(account.idname+"账户余额为"+account.money); System.out.println(Thread.currentThread().getName()+"手里的钱为"+nowMoney); } }
改后:
public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"张三"); Drawing you =new Drawing(account,50,"boyfriend"); Drawing girlfriend =new Drawing(account,100,"girlfriend"); you.start(); girlfriend.start(); } } //账户 class Account{ int money;//余额 String idname;//卡名 public Account(int money, String idname) { this.money = money; this.idname = idname; } } //银行 class Drawing extends Thread{ Account account; int drawingMoney; int nowMoney; public Drawing(Account account,int drawingMoney,String idname){ super(idname); this.account =account; this.drawingMoney = drawingMoney; } @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) { throw new RuntimeException(e); } account.money = account.money - drawingMoney; nowMoney = nowMoney + drawingMoney; System.out.println(account.idname+"账户余额为"+account.money); System.out.println(Thread.currentThread().getName()+"手里的钱为"+nowMoney); } } }
死锁
-
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而堵塞时,对以获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺
- 循环等待条件:若干进程意见形成一种头尾相接的循环等待资源关系
-
避免死锁条件:只要想办法破其中任意一个或者多个条件就可以避免死锁发生