Java public static void main(String[] args) throws Exception { //目标:创建线程第3种方式:实现Callable接口
//一个子线程完成任务呢 long start = System.currentTimeMillis(); //3.创建Callable对象 MyCallable myCallable = new MyCallable(0,1500); //4.创建FutureTask任务对象,传入Callable对象 FutureTask<Integer> f1 = new FutureTask<>(myCallable); //5.创建线程对象,基于FutureTask创建,并启动线程 new Thread(f1).start(); //6.获取线程结果 Integer result = f1.get();//会阻塞等待子线程运行完成得到结果才会往下执行主线程 long end = System.currentTimeMillis(); System.out.println("子线程运行结果:"+result+",共耗时"+(end-start)+"ms"); //得出结论:一个线程做需要4000多毫秒 } //1.定义实现Callable接口 class MyCallable implements Callable<Integer> { private Integer start; private Integer end; public MyCallable(Integer start, Integer end) { this.start = start; this.end = end; }
//2.重写call方法 @Override public Integer call() throws Exception { int sum = 0; for (Integer i = start; i <= end; i++) { sum += i; System.out.println("第"+i+"次累加,结果"+sum); Thread.sleep(2); } return sum; } }
用三个线程计算:
Java public static void main(String[] args) throws Exception { //目标:创建线程第3种方式:实现Callable接口
// 多个子线程完成这个计算任务,第一个线程完成0~500,第二个线程完成501~1000,第三个线程完成1001~1500 long start = System.currentTimeMillis(); //第一个线程任务 FutureTask<Integer> f1 = new FutureTask<>(new MyCallable(0, 500)); new Thread(f1).start(); FutureTask<Integer> f2 = new FutureTask<>(new MyCallable(501, 1000)); new Thread(f2).start(); FutureTask<Integer> f3 = new FutureTask<>(new MyCallable(1001, 1500)); new Thread(f3).start();
//6.获取线程结果 Integer result1 = f1.get(); Integer result2 = f2.get(); Integer result3 = f3.get(); long end = System.currentTimeMillis(); System.out.println("计算结果:"+(result1+result2+result3)+",共耗时"+(end-start)+"ms"); //得出结果:3个线程做需要1000多毫秒 }
//2.重写call方法 @Override public Integer call() throws Exception { int sum = 0; for (Integer i = start; i <= end; i++) { sum += i; System.out.println("第"+i+"次累加,结果"+sum); Thread.sleep(2); } return sum; } }
Java public class Test { public static void main(String[] args) throws InterruptedException { //目标:理解线程常用的方法 MyThread myThread = new MyThread("aa"); myThread.start();
Thread bb = new Thread(()->{ for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"正在运行"); try { Thread.sleep(10);//让线程休眠挂起,线程执行会阻塞,模拟线程执行较长时间 } catch (InterruptedException e) { throw new RuntimeException(e); } } },"bb"); bb.start();
//Thread.sleep(10); //Thread.currentThread() 获取当前执行的线程对象Thread //Thread对象.getName() 获取线程名称 for (int i = 0; i < 10; i++) { if(i==3){ bb.join();//阻塞等待当前线下运行完当前线程(主线程)才能继续运行(优先运行当前子线程) } System.out.println(Thread.currentThread().getName()+"正在运行"); Thread.sleep(10); } } } //1.定义类,继承Thread class MyThread extends Thread { public MyThread(String name) { super(name);//给线程起名字 } public void run() { System.out.println(Thread.currentThread().getName()+"子线程运行。。。");//thread-0,如果起名字就按照起的名字 } }
线程安全(面试高频)
认识线程安全
多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。
线程安全出现的原因:
存在多个线程在同时执行
同时访问一个共享资源
存在修改该共享资源
模拟线程安全问题
小明和小红是一对夫妻,他们有一个共同的账户,余额是10万元,用程序模拟两人同时取钱10万元。
Java Account @Data @NoArgsConstructor @AllArgsConstructor public class Account { private String cardNo;// 卡号 private double money;// 余额 }
Java MyRunnable public class MyRunnable implements Runnable{ private Account account; private ATM atm; public MyRunnable(Account account, ATM atm) { this.account = account; this.atm = atm; }
@Override public void run() { atm.drawMoney(account,100000); } }
Java Test public class Test { public static void main(String[] args) { //目标:模拟小红、小明取钱 //1.创建一个账户 Account account = new Account("ICBC-123456", 100000.0);
//2、创建一个ATM机给小明 ATM atm1 = new ATM();
//3、创建一个ATM机给小红 ATM atm2 = new ATM();
//4、创建第一个线程让第一个ATM执行取钱任务 new Thread(new MyRunnable(account, atm1),"小明").start();
//5、创建第二个线程让第二个ATM执行取钱任务 new Thread(new MyRunnable(account, atm2),"小红").start(); } }
线程同步
线程同步是线程安全问题的解决方案。
让多个线程先后依次访问共享资源,这样就可以避免出现线程安全问题。
认识线程同步
常用的线程同步方案:
加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。
方式一:同步代码块
Java synchronized(同步锁) { 访问共享资源的核心代码(不安全的代码) }
作用:把访问共享资源的核心代码给上锁,以此保证线程安全。
原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行。
Java Account @Data @NoArgsConstructor @AllArgsConstructor public class Account { private String cardNo;// 卡号 private double money;// 余额 }
Java MyRunnable public class MyRunnable implements Runnable{ private Account account; private ATM atm; public MyRunnable(Account account, ATM atm) { this.account = account; this.atm = atm; }
@Override public void run() { atm.drawMoney(account,100000); } }
Java Test public class Test { public static void main(String[] args) { //目标:模拟小红、小明取钱 //1.创建一个账户 Account account = new Account("ICBC-123456", 100000.0);
//2、创建一个ATM机给小明 ATM atm1 = new ATM();
//3、创建一个ATM机给小红 ATM atm2 = new ATM();
//4、创建第一个线程让第一个ATM执行取钱任务 new Thread(new MyRunnable(account, atm1),"小明").start();
//5、创建第二个线程让第二个ATM执行取钱任务 new Thread(new MyRunnable(account, atm2),"小红").start();
//第二对夫妻 // 1、创建一个共享账户 Account account2 = new Account("ICBC-123456", 100000.0);
// 2、创建一个ATM机给小明用 ATM atm3 = new ATM();
// 3、创建一个ATM机给小红用 ATM atm4 = new ATM();
// 4. 创建第一个线程让第一个ATM执行取钱任务 new Thread(new MyRunnable(account2, atm3), "小张").start();
// 5. 创建第二个线程让第二个ATM执行取钱任务 new Thread(new MyRunnable(account2, atm4), "小李").start(); } }
//2.重写call方法执行任务代码 @Override public Integer call() throws Exception { int sum = 0; for (Integer i = start; i <= end; i++) { sum += i; System.out.println("第"+i+"次累加,结果:"+sum); Thread.sleep(2);