public class Main {
public static void main(String[] args) {
//3.创建线程对象
MyThread mt = new MyThread();
//4.启动线程,调用start()方法(运行的是run方法)
mt.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程:"+i);
}
}
}
//1.定义一个线程类
class MyThread extends Thread{
//2.重写run方法,里面定义线程启动后做什么
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程:"+i);
}
}
}
缺点:线程类已经继承Thread,无法继承其他类,不利于扩展
为什么不直接调用run方法,而是调用start启动线程
public class Main {
public static void main(String[] args) {
//3.创建任务类对象
//MyAsign ma = new MyAsign();
Runnable rn = new MyAsign();
//4.吧任务交给Thread,通过线程类,启动该任务
new Thread(rn).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程:"+i);
}
}
}
//1.定义一个线程任务类,实现Runnable接口
class MyAsign implements Runnable{
//2.重写run方法,定义线程的执行内容
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程:"+i);
}
}
}
缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的
匿名内部类形式
public class Demo {
public static void main(String[] args) {
//1.使用匿名内部类的方式,重写Runnable中的方法
// Runnable rn = new Runnable() {
// @Override
// public void run() {
// for (int i = 0; i < 5; i++) {
// System.out.println("子线程:"+i);
// }
// }
// };
//2.创建Thread类,并吧该匿名内部类重写后交给线程类来处理
//new Thread(rn).start();
new Thread(() ->{
for (int i = 0; i < 5; i++) {
System.out.println("子线程:"+i);
}
}).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程:"+i);
}
}
}
前两种线程方式存在问题
实现步骤:利用Callable、FutureTask接口实现
public class Demo {
public static void main(String[] args) throws Exception{
//3.创建任务类对象,
MyCallable mc = new MyCallable(100);
//4.将Callable任务对象交给FutureTask
/*
FutureTask作用1:是Runnable的对象(实现了Runnable接口),可以将它交给Thread线程类
FutureTask作用2:可以在线程执行完成后,使用get方法获取线程执行的结果
*/
FutureTask<String> ft = new FutureTask<>(mc);
//5.交给线程对象处理
new Thread(ft).start();
MyCallable mc1 = new MyCallable(200);
FutureTask<String> ft1 = new FutureTask<>(mc1);
new Thread(ft1).start();
System.out.println(ft.get());
System.out.println(ft1.get());
}
}
//1.定义任务类,来实现Callable接口
class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
//2.重写call方法
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 0; i < n; i++) {
sum+=i;
}
return "子线程的执行结果是:"+sum;
}
}
【MyThread】
public class MyTread extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"线程:"+i);
}
}
}
【Main】
public class Main {
public static void main(String[] args) {
MyTread t1 = new MyTread();
t1.setName("No.1");
t1.start();
System.out.println(t1.getName());
MyTread t2 = new MyTread();
t2.setName("No.2");
t2.start();
System.out.println(t2.getName());
//谁执行它,它就得到哪个线程的名字
System.out.println(Thread.currentThread().getName());
for (int i = 0; i < 5; i++) {
System.out.println("main线程输出:"+i);
}
}
}
【Thread类提供的:yield、join、interrupt、不推荐的方法 stop、守护线程等等,在开发中很少使用。】
public class Demo {
public static void main(String[] args) throws Exception{
for (int i = 0; i < 5; i++) {
System.out.println("主线程:"+i);
if(i == 3){
Thread.sleep(3000);//毫秒
}
}
}
}
【Account】
public class Account {
private String cardID;
private double money;
//取钱功能
public void takeMoney(double money){
//获取谁来取钱
String name = Thread.currentThread().getName();
//判断是否足够金额
if(this.money >= money){
System.out.println(name+"来取钱,取出:"+money);
//更新余额
this.money -= money;
System.out.println(name+"取钱后,剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足!");
}
}
public String getCardID() {
return cardID;
}
public void setCardID(String cardID) {
this.cardID = cardID;
}
public Account(String cardID, double money) {
this.cardID = cardID;
this.money = money;
}
public Account() {
}
}
【MyThread】
public class MyThread extends Thread{
private Account acc;
public MyThread(Account acc,String name){
super(name);
this.acc = acc;
}
@Override
public void run() {
//取钱
acc.takeMoney(100000);
}
}
【Demo】
public class Demo {
public static void main(String[] args) throws Exception{
Account account = new Account("IQBQ",100000);
new MyThread(account,"小红").start();
new MyThread(account,"小明").start();
}
}
【碰到的结果:两人都取了10万,银行亏了10万】
解决
核心思想
同步代码块
锁对象要求
【Demo与MyThread同上】
【Account修改取钱功能】
public void takeMoney(double money){
//获取谁来取钱
String name = Thread.currentThread().getName();
//判断是否足够金额
//吧当前取钱的账号作为共享账户
synchronized (this) {
if(this.money >= money){
System.out.println(name+"来取钱,取出:"+money);
//更新余额
this.money -= money;
System.out.println(name+"取钱后,剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足!");
}
}
}
【修改Account取钱方法】
//取钱功能
public synchronized void takeMoney(double money){
//获取谁来取钱
String name = Thread.currentThread().getName();
//判断是否足够金额
if(this.money >= money){
System.out.println(name+"来取钱,取出:"+money);
//更新余额
this.money -= money;
System.out.println(name+"取钱后,剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足!");
}
}
【修改Account取钱方法】
//取钱功能
public void takeMoney(double money){
//获取谁来取钱
String name = Thread.currentThread().getName();
//判断是否足够金额
//上锁
rl.lock();
try {
if(this.money >= money){
System.out.println(name+"来取钱,取出:"+money);
//更新余额
this.money -= money;
System.out.println(name+"取钱后,剩余:"+this.money);
}else {
System.out.println(name+"来取钱,余额不足!");
}
} finally {
//解锁
rl.unlock();
}
}
【需要try finally原因:一旦上锁后,没有解锁之前遇到了异常,则锁会一直处于上锁状态,其他线程无法再进入】
线程通信实际应用场景
线程通信的前提
【注意:wati()是解锁并等待自己,而sleep()是不解锁单纯的等待自己】
【Account】
public class Account {
private String cardID;
private double money;
public Account(String cardID, double money) {
this.cardID = cardID;
this.money = money;
}
public Account() {
}
//取钱
public synchronized void takeMoney(double money){
try{
String name = Thread.currentThread().getName();
if(this.money >= money){
this.money -= money;
System.out.println(name+"来取"+money+"钱,取完剩余:"+this.money);
//叫醒其他线程,自己等待
this.notifyAll();
this.wait();
}else {
//没钱的情况下,叫醒其他线程,自己等待
this.notifyAll();
this.wait();
}
}catch (Exception e){
e.printStackTrace();
}
}
//存钱
public synchronized void saveMoney(double money){
try{
String name = Thread.currentThread().getName();
if(this.money == 0){
this.money += money;
System.out.println(name+"来存钱,存钱后余额为:"+this.money);
//存钱后唤醒其他线程,等待自己
this.notifyAll();
this.wait();
}else {
//已经有钱了,唤醒其他线程,等待自己
this.notifyAll();
this.wait();
}
}catch (Exception e){
e.printStackTrace();
}
}
public String getCardID() {
return cardID;
}
public void setCardID(String cardID) {
this.cardID = cardID;
}
}
【MyThread】-【TakeThread】
public class MyThread extends Thread{
private Account acc;
public MyThread(Account acc, String name){
super(name);
this.acc = acc;
}
@Override
public void run() {
while(true){
acc.takeMoney(100000);
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
【SaveThread】
public class SaveThread extends Thread{
private Account account;
public SaveThread(Account account,String name){
super(name);
this. account = account;
}
@Override
public void run() {
//存钱
while(true){
account.saveMoney(100000);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
【Demo】
public class Demo {
public static void main(String[] args) {
Account children = new Account("QQ",0);
//创建两个线程,为取钱
new MyThread(children,"小红").start();
new MyThread(children,"小明").start();
//三个线程,为存钱
new SaveThread(children,"父亲").start();
new SaveThread(children,"干爹").start();
new SaveThread(children,"岳父").start();
}
}
线程池接口
如何得到线程池对象
【MyRunnable】
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"正在输出,输出:Hello ====>"+i);
}
try {
System.out.println(Thread.currentThread().getName()+"进入休眠状态!!!!");
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
【ThreadPool】
public class ThreaPool {
public static void main(String[] args) {
//创建线程池
ExecutorService es = new ThreadPoolExecutor(3,5,6,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//执行线程
MyRunnable target = new MyRunnable();
//执行了三个核心线程(核心线程是一直存活的)
es.execute(target);
es.execute(target);
es.execute(target);
//截至,五个队列任务已经满
es.execute(target);
es.execute(target);
es.execute(target);
es.execute(target);
es.execute(target);
//开始创建临时线程
es.execute(target);
es.execute(target);
//拒绝任务,拒绝策略机制触发
es.execute(target);
//es.shutdownNow();//立即关闭线程池,不管任务是否完成(通常不会用到)
//es.shutdown();//关闭线程池,等待任务完成以后关闭(也不会用到)
}
}
【MyCallable】
public class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 0; i <= n; i++) {
sum += i;
}
return Thread.currentThread().getName()+"执行:1 - "+n+"和的结果:"+sum;
}
}
【Demo】
public class Demo {
public static void main(String[] args) throws Exception{
//创建线程池
ExecutorService es = new ThreadPoolExecutor(3,5,6,
TimeUnit.SECONDS,new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//执行线程
Future<String> future1 = es.submit(new MyCallable(100));
Future<String> future2 = es.submit(new MyCallable(200));
Future<String> future3 = es.submit(new MyCallable(300));
Future<String> future4 = es.submit(new MyCallable(400));
System.out.println(future1.get());
System.out.println(future2.get());
System.out.println(future3.get());
System.out.println(future4.get());
}
}
【注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象的】
定时器
实现方式一:Timer
实现方式二:ScheduleExecutorService
public class Demo {
public static void main(String[] args) {
ScheduledExecutorService pool = new ScheduledThreadPoolExecutor(3);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了语句一次:AAA"+new Date());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},0,2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了语句一次:BBB"+new Date());
System.out.println(10/0);
}
},0,2, TimeUnit.SECONDS);
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"执行了语句一次:CCC"+new Date());
}
},0,2, TimeUnit.SECONDS);
}
}