一、基础知识
进程(Pocess)和线程(Thread)
1.为什么要使用多线程?
先总体上:
- 从计算机底层来说:线程可以比作是轻量级的进程,是程序执行的最小单元,线程间的切换和调度的成本远远小于进程。另外,多核 CPU 时代意味着多个线程可以同时运行,这减少了线程上下文切换的开销。
- 从当代互联网发展趋势来说:现在的系统动不动就要求百万级甚至千万级的并发量,而多线程并发编程正式开发高并发系统的基础,利用好多线程机制可以大大提高系统的并发能力以及性能。
再深入到计算机底层:
- 单核时代:在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利用率。
- 多核时代:多核时代主要是为了提高 CPU 的利用率。
2.使用多线程可能会带来什么问题?
并发编程的目的就是为了能提高程序的执行效率提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而并发编程可能会遇到很多问题,比如:内存泄漏、上下文切换、死锁等,还有受限于硬件和软件和资源闲置问题。
二、创建线程的方式
1. Thread
Thread实现多线程url地址下载文件
首先下载一个commons-io的jar包
//实现多线程下载文件
public class Thread01 extends Thread{
private String url;
private String filename;
public Thread01(String url, String filename) {
this.url = url;//文件地址
this.filename = filename;//文件保存的名字
}
//下载文件线程的执行体
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downloader(url,filename);
System.out.println("下载了"+filename);
}
public static void main(String[] args) {
Thread01 t1 = new Thread01("https://googleads.g.doubleclick.net/pagead/html/r20200707/r20190131/zrt_lookup.html#","1.html");
t1.start();
Thread01 t2 = new Thread01("https://res.wx.qq.com/a/wx_fed/assets/res/OTE0YTAw.png","2.png");
t2.start();
Thread01 t3 = new Thread01("https://res.wx.qq.com/a/wx_fed/assets/res/OTE0YTAw.png","3.png");
t3.start();
}
}
//下载文件的类
class WebDownLoader{
//下载文件的方法
public void downloader(String url,String filename){
try {
FileUtils.copyURLToFile(new URL(url),new File(filename));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常!");
}
}
}
2.Runnable
3.实现Callable接口
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable01 c1 = new Callable01("https://res.wx.qq.com/a/wx_fed/assets/res/OTE0YTAw.png", "1.png");
Callable01 c2 = new Callable01("https://res.wx.qq.com/a/wx_fed/assets/res/OTE0YTAw.png", "2.png");
Callable01 c3 = new Callable01("https://res.wx.qq.com/a/wx_fed/assets/res/OTE0YTAw.png", "3.png");
//创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> f1 = executorService.submit(c1);
Future<Boolean> f2 = executorService.submit(c2);
Future<Boolean> f3 = executorService.submit(c3);
//获取结果
Boolean b1 = f1.get();
Boolean b2 = f2.get();
Boolean b3 = f3.get();
//关闭服务
executorService.shutdownNow();
}
三、并发问题
并发和并行有什么区别?
- 并发:同一时间段,多个任务都在执行(单位时间内不一定同时执行);
- 并行:单位时间内,多个任务同时执行。
并发的关键是你有处理多个任务的能力,不一定要同时。 而并行的关键是你有同时处理多个任务的能力。
龟兔赛跑问题
//模拟龟兔赛跑并发问题
public class Runnable01 implements Runnable{
private String winner;
@Override
public void run() {
for (int i=0;i<=100;i++){
//模拟兔子休息
if(Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
//判断是是否完成比赛
private boolean gameOver(int steps){
//判断是否有获胜者
if (winner != null){
return true;
}{if(steps == 100) {//100步获胜
winner = Thread.currentThread().getName();
System.out.println("获胜者是**" + winner + "**");
return true;
}
}
return false;
}
public static void main(String[] args) {
Runnable runnable = new Runnable01();
new Thread(runnable,"兔子").start();
new Thread(runnable,"乌龟").start();
}
}
四、线程
停止线程的方式
线程睡眠
public class Time {
public static void main(String[] args) throws InterruptedException {
tenDown();
time();
}
//模拟倒计时
static void tenDown(){
int num=10;
while(true){
System.out.println(num);
num--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (num <= 0){
break;
}
}
}
//获取当前时间,每秒刷新一次
static void time() throws InterruptedException {
while(true){
//获取当前时间
Date date = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("YYYY/MM/dd HH:mm:ss").format(date));
Thread.sleep(1000);
}
}
}
线程礼让(yield)
线程强制执行(join)
线程状态观测(State)
public class Thread02 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
//睡5秒
for (int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程结束!");
});
//观察启动前状态
Thread.State state = thread.getState();
System.out.println("现在的状态是:"+state);//NEW
//观察启动后状态
thread.start();
state = thread.getState();
System.out.println("现在的状态是:"+state);//RUNNABLE
//只要
while (state != Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState();
System.out.println(state);//TIMED_WAITING
}
}
}
线程优先级(Priority)
public class Priority {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
Mypriorrity mypriorrity = new Mypriorrity();
Thread t1 =new Thread(mypriorrity,"t1");
Thread t2 =new Thread(mypriorrity,"t2");
Thread t3 =new Thread(mypriorrity,"t3");
Thread t4 =new Thread(mypriorrity,"t4");
//不设置优先级,默认是5
t1.start();
//设置优先级
t2.setPriority(1);
t2.start();
t3.setPriority(10);
t3.start();
t4.setPriority(7);
t4.start();
}
}
class Mypriorrity implements Runnable{
@Override
public void run() {
//打印 线程名字-->线程优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
守护线程(daemon)
//守护线程
public class Daemon {
public static void main(String[] args) {
Thread god = new Thread(new god());
god.setDaemon(true);//默认是false,默认是守护线程
god.start();
Thread person = new Thread(new person());
person.start();
}
}
//神
class god implements Runnable{
@Override
public void run() {
while (true){
System.out.println("神与你同在!");
}
}
}
//人
class person implements Runnable{
@Override
public void run() {
//人生不过三万天
for (int i=0;i<=30000;i++){
System.out.println("===还活着===");
}
System.out.println("-------------GoodBye,World!----------------");
}
}
不安全线程问题
//不安全的取钱
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account("学习基金",100);
Bank take1 = new Bank(account,50,"沈云飞1");
Bank take2 = new Bank(account,100,"沈云飞2");
new Thread(take1,"沈云飞").start();
new Thread(take2,"syf").start();
}
}
//用户
class Account{
String name;
int money;
public Account(String name,int money){
this.money=money;
this.name=name;
}
}
//银行 模拟取钱
class Bank implements Runnable{
Account account;//账户
int takeMoney;//取多少钱
int lastMoney;//手里有多少钱
public Bank(Account account, int takeMoney, String name) {
this.account = account;
this.takeMoney = takeMoney;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(account.money - takeMoney < 0){
System.out.println(Thread.currentThread().getName()+" 的余额不足!");
}
System.out.println("您的账户有"+account.money+"万刀");
//卡内余额
account.money = account.money - takeMoney;
//取到的钱
lastMoney = lastMoney + takeMoney;
System.out.println(Thread.currentThread().getName()+" 您取到的钱为:"+lastMoney);
System.out.println(Thread.currentThread().getName()+" 卡内余额为:"+account.money);
}
}
线程同步
改良上边的方法加个同步块
死锁
他们会互相强占对方拥有的资源,无法进行自己后边的操作
解决办法,把自己第二个锁从第一个锁里拿出来就行了.
Lock锁
public class Lock {
public static void main(String[] args) {
Tricket tricket = new Tricket();
new Thread(tricket).start();
new Thread(tricket).start();
new Thread(tricket).start();
}
}
class Tricket implements Runnable{
int num = 9;
//定义Lock锁
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if(num>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num);
num--;
}else break;
}finally {
lock.unlock();//解锁
}
}
}
}
synchronized 和 Lock 的对比
线程池
1.Runnable
//Runnable接口线程池
public class Pool {
public static void main(String[] args) {
//1.创建服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
//2.启动
executorService.submit(new MyThread());
executorService.submit(new MyThread());
executorService.submit(new MyThread());
executorService.submit(new MyThread());
//3.关闭
executorService.shutdownNow();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
因为Executors.newFixedThreadPool(3); 所以最大线程只是3个
2.Callable
//Callable接口线程池
public class Pool02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//执行服务
ExecutorService executorService = Executors.newFixedThreadPool(4);
//提交执行
Future submit = executorService.submit(new MyThread01());
Future submit2 = executorService.submit(new MyThread01());
Future submit3 = executorService.submit(new MyThread01());
Future submit4 = executorService.submit(new MyThread01());
//获取结果
Object o = submit.get();
//关闭服务
executorService.shutdownNow();
}
}
class MyThread01 implements Callable {
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName());
return null;
}
}