Java核心类库篇7——多线程
1、程序、进程和线程
- 程序 - 数据结构 + 算法,主要指存放在硬盘上的可执行文件
- 进程 - 主要指运行在内存中的可执行文件
- 线程就是进程内部的程序流
操作系统内部支持多 进程的,而每个进程的内部又是支持多线程的
2、线程的创建
方法声明 | 功能介绍 |
---|---|
public Thread() | 使用无参的方式构造对象 |
public Thread(String name) | 根据参数指定的名称来构造对象 |
public Thread(Runnable target) | 根据参数指定的引用来构造对象,其中Runnable是个接口类型 |
public Thread(Runnable target, String name) | 根据参数指定引用和名称来构造对象 |
public void run() | 若使用Runnable引用构造了线程对象,调用该方法时最终调 用接口中的版本 若没有使用Runnable引用构造线程对象,调用该方法时则啥也不做 |
public void start() | 用于启动线程,Java虚拟机会自动调用该线程的run方法 |
public long getId() | 获取调用对象所表示线程的编号 |
public String getName() | 获取调用对象所表示线程的名称 |
public void setName(String name) | 设置/修改线程的名称为参数指定的数值 |
public static Thread currentThread() | 获取当前正在执行线程的引用 |
2.1、继承Thread类
- 优点:实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程
- 缺点:线程类已经继承Thread类了,就不能再继承其他类,多个线程不能共享同一份资源
public class MyThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 50; i++) {System.out.println(this.getName()+"----------------"+i);}}
}
public class Test {public static void main(String[] args) {new MyThread().start();for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName()+"----------------"+i);}}
}
注:直接调用run方法如同调用类成员方法一样
2.2、实现Runnable接口
- 优点:线程类只是实现了接口,还可以继承其他类,多个线程可以使用同一个target对象,适合多个线程处理同一份资源的情况
- 缺点:通过这种方式实现多线程,相较于第一类方式,编程较复杂,要访问当前线程,必须调用Thread.currentThread()方法
public class MyRunable implements Runnable {@Overridepublic void run() {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName()+"----------------"+i);}}
}
public class Test {public static void main(String[] args) {new Thread(new MyRunable()).start();for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName()+"----------------"+i);}}
}
2.3、Callable和FutureTask
- 优点:线程类只是实现了接口,还可以继承其他类,多个线程可以使用同一个target对象,适合多个线程处理同一份资源的情况
- 缺点:通过这种方式实现多线程,相较于第一类方式,编程较复杂,要访问当前线程,必须调用Thread.currentThread()方法
public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<Integer> futureTask=new FutureTask<Integer>(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName()+"----------------"+i);}return 100;}});new Thread(futureTask, "ruoye").start();for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName()+"----------------"+i);}System.out.println(futureTask.get());}
}
lambda表达式
public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {FutureTask<Integer> futureTask = new FutureTask<>((Callable<Integer>) () -> {System.out.println("hello world!");return 100;});new Thread(futureTask, "ruoye").start();for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName()+"----------------"+i);}System.out.println(futureTask.get());}
}
3、线程优先级及线程让步
方法声明 | 功能介绍 |
---|---|
public static void yield() | 当前线程让出处理器(离开Running状态),使当前线程进入Runnable 状态等待 |
public static void sleep(times) | 使当前线程从 Running 放弃处理器进入Block状态, 休眠times毫秒 |
public int getPriority() | 获取线程的优先级 |
public void setPriority(int newPriority) | 修改线程的优先级,优先级越高的线程不一定先执行,但该线程获取到时间片的机会会更多 一些 |
public void join() | 等待该线程终止 |
public void join(long millis) | 等待参数指定的毫秒数 |
public boolean isDaemon() | 用于判断是否为守护线程 |
public void setDaemon(boolean on) | 用于设置线程为守护线程 |
sleep
public class Test {public static void main(String[] args) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");while (true){System.out.println(simpleDateFormat.format(new Date()));try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
setPriority线程优先级
public class Test {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println(Thread.currentThread().getName()+"优先级-----"+Thread.currentThread().getPriority());for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"-----"+i);}});thread.setName("ruoye");thread.setPriority(Thread.MAX_PRIORITY);thread.start();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"-----"+i);}}
}
线程等待
public class Test {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"-----"+i);}});thread.setName("ruoye");thread.start();
// thread.join();
// System.out.println("终于等到你");thread.join(5000);System.out.println("没有等到你");}
}
守护线程
public class Test {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"-----"+i);}});thread.setName("ruoye");thread.setDaemon(true);thread.start();Thread.sleep(5000);System.out.println("没有等到你");}
}
4、线程同步
4.1、多线程出现的问题
public class Account implements Runnable {private int money;public Account(int money) {this.money = money;}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}@Overridepublic void run() {System.out.println("进到门口");System.out.println("开始取钞");if (this.money>200){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}this.money-=200;System.out.println("取款成功");}}@Overridepublic String toString() {return "Account{" +"money=" + money +'}';}
}
public class Test {public static void main(String[] args) throws InterruptedException {Account account = new Account(1000);Thread thread = new Thread(account);Thread thread1 = new Thread(account);thread.start();thread1.start();thread.join();thread1.join();System.out.println(account.getMoney());}
}
4.2、synchronized同步锁
4.2.1、synchronized代码块
下面所示为锁class,锁Account对象里的成员变量(对象)也可,但请时刻记住,多个Account为对象里的成员变量(对象)多个对象,那么就拥有了多把锁,此时应当用static修饰
休眠在同步代码块内不会让出cpu
public class Account implements Runnable {private int money;public Account(int money) {this.money = money;}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}@Overridepublic void run() {System.out.println("进到门口");synchronized (Account.class){System.out.println("开始取钞");if (this.money>200){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}this.money-=200;System.out.println("取款成功");}}}@Overridepublic String toString() {return "Account{" +"money=" + money +'}';}
}
public class Test {public static void main(String[] args) throws InterruptedException {Account account = new Account(1000);Thread thread = new Thread(account);Thread thread1 = new Thread(account);thread.start();thread1.start();thread.join();thread1.join();System.out.println(account.getMoney());}
}
4.2.2、synchronized方法
当synchronized位于成员方法上等价于synchronized (this)
当当synchronized位于成员方法上等价于synchronized (类对象)
public class Account implements Runnable {private int money;public Account(int money) {this.money = money;}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}@Overridepublic synchronized void run() {System.out.println("进到门口");System.out.println("开始取钞");if (this.money>200){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}this.money-=200;System.out.println("取款成功");}}@Overridepublic String toString() {return "Account{" +"money=" + money +'}';}
}
public class Test {public static void main(String[] args) throws InterruptedException {Account account = new Account(1000);Thread thread = new Thread(account);Thread thread1 = new Thread(account);thread.start();thread1.start();thread.join();thread1.join();System.out.println(account.getMoney());}
}
4.3、死锁问题
尽量减少同步的资源,减少同步代码块的嵌套结构的使用
线程一执行的代码
public void run(){synchronized(a){ //持有对象锁a,等待对象锁b synchronized(b){ //编写锁定的代码; }}
}
线程二执行的代码
public void run(){synchronized(b){ //持有对象锁a,等待对象锁b synchronized(a){ //编写锁定的代码; }}
}
4.4、Lock锁
- Lock是显式锁,需要手动实现开启和关闭操作,而synchronized是隐式锁,执行锁定代码后自动释放
- Lock只有同步代码块方式的锁,而synchronized有同步代码块方式和同步方法两种锁
- 使用Lock锁方式时,Java虚拟机将花费较少的时间来调度线程,因此性能更好
public class Account implements Runnable {private int money;private static Lock lock=new ReentrantLock();public Account(int money) {this.money = money;}public int getMoney() {return money;}public void setMoney(int money) {this.money = money;}@Overridepublic void run() {lock.lock();System.out.println("进到门口");System.out.println("开始取钞");if (this.money>200){try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}this.money-=200;System.out.println("取款成功");}lock.unlock();}@Overridepublic String toString() {return "Account{" +"money=" + money +'}';}
}
public class Test {public static void main(String[] args) throws InterruptedException {Account account = new Account(1000);Thread thread = new Thread(account);Thread thread1 = new Thread(account);thread.start();thread1.start();thread.join();thread1.join();System.out.println(account.getMoney());}
}
5、线程通信
5.1、线程通信
不能锁class
public class Account implements Runnable {private int a;public Account(int a) {this.a = a;}@Overridepublic void run() {while (true){synchronized (this) {if (a<100){System.out.println(Thread.currentThread().getName()+"========"+a);a++;notify();try {wait();} catch (InterruptedException e) {e.printStackTrace();}}else{break;}}}}
}
public class Test {public static void main(String[] args) throws InterruptedException {Account account = new Account(1);Thread ruoye = new Thread(account, "ruoye");Thread yoya = new Thread(account, "yoya");ruoye.start();yoya.start();}
}
5.2、生产者消费者问题
- 线程间的通信共享数据一定要有同步代码块synchronized
- 一定要有wait和notify,而且二者一定是成对出现
- 生产者和消费者的线程实现一定是在while(true)里面
public class Mother implements Runnable {private Account account;public Mother(Account account) {this.account = account;}@Overridepublic void run() {while (true){try {account.produce();Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Ming implements Runnable {private Account account;public Ming(Account account) {this.account = account;}@Overridepublic void run() {while (true){try {account.consumer();Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class Account {private int a;public Account(int a) {this.a = a;}public int getA() {return a;}public void setA(int a) {this.a = a;}public synchronized void produce() throws InterruptedException {notify();if (a<2000){a+=100;System.out.println("妈妈给小明存了100元");}else{wait();}}public synchronized void consumer() throws InterruptedException {notify();if (a>75){a-=75;System.out.println("小明花了75元");}else{wait();}}
}
public class Test {public static void main(String[] args) throws InterruptedException {Account account = new Account(0);new Thread(new Mother(account)).start();new Thread(new Ming(account)).start();}
}