多线程基础
线程:一个程序内部的一条执行流程,只有一条执行流程就是单线程
java.lang.Thread代表线程
主线程退出,子线程存在,进程不会退出
可以使用jconsole
查看
创建线程
有多个方法可以创建线程
-
继承Thread类
- 优点:编码简单
- 缺点:无法继承其他类,不利于功能的扩展
-
实现Runnable接口
- 优点:任务类只是实现了接口,可以继续继承其他类、实现其他接口,扩展性强
- 缺点:需要多创建一个Runnable对象
-
实现Callable接口和FutureTask类
- 优点:可以返回线程执行结束之后的结果
- 缺点:编码复杂
执行为什么是start()?
使用run不是多线程, 相当于直接调用方法 还是单线程
start->start0(本地方法 JVM调用 C/C++实现的)
方法一
public class Demo1 {
public static void main(String[] args) throws Exception {
//main是主线程执行的
//新建了一个t线程
Thread t = new primeThread();
//启动线程 start自动调用run方法 必须要调用start方法
//如果是t.run() 相当于直接调用方法 还是单线程
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程");
Thread.sleep(500);
}
}
}
class primeThread extends Thread{
public primeThread(){
}
@Override
public void run() {
//描述线程的执行的任务
for (int i = 0; i < 5; i++) {
System.out.println("子线程");
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
方法二
public class Demo2 {
public static void main(String[] args) throws Exception {
//runnable只是一个任务对象
Runnable target = new prime1Thread();
//需要线程对象接受任务对象 开辟新的线程
new Thread(target).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程");
Thread.sleep(500);
}
}
}
class prime1Thread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程");
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
//可以使用匿名内部类
public class Demo2 {
public static void main(String[] args) throws Exception {
//需要线程对象进行调用任务对象开辟新的线程
new Thread(()-> {
for (int i = 0; i < 5; i++) {
System.out.println("子线程");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程");
Thread.sleep(500);
}
}
}
方法三
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建一个Callable对象
Callable<String> myCallable = new MyCallable(100);
// 把Callable的对象封装成一个FutureTask对象(任务对象)
// 未来任务对象的作用?
// 1、是一个任务对象,实现下Runnable对象
// 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕的结果
//也可以使用匿名内部类
FutureTask<String> stringFutureTask = new FutureTask<>(myCallable);
new Thread(stringFutureTask).start();
//获取结果会阻塞线程
System.out.println(stringFutureTask.get());
}
}
//泛型
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 = 1; i <= n; i++) {
sum+=i;
}
return sum+"";
}
}
线程方法
setPriority()
更改线程的优先级getPriority()
获取线程的优先级interrupt
中断线程,并不是真正的结束线程 所以一般用于中断正在休眠的线程yield
线程的礼让,不一定礼让成功(和join
相反,线程的插队)
public class Demo4 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread1("1号线程");
// t1.setName("1号线程");//启动之前取名字
t1.start();
t1.join();
// System.out.println(t1.getName());
Thread t2 = new Thread1("2号线程");
// t2.setName("2号线程");//启动之前取名字
t2.start();
t2.join();//t2线程执行完成之后才能继续往下执行
// System.out.println(t2.getName());
Thread t3 = new Thread1("3号线程");
t3.start();
t3.join();
Thread m = Thread.currentThread();
m.setName("最牛逼的名字");
// System.out.println(m.getName());
for (int i = 0; i < 5; i++) {
System.out.println(m.getName()+"输出"+(i+1));
}
}
}
class Thread1 extends Thread{
public Thread1(String name) {
super(name);
}
@Override
public void run() {
Thread t= Thread.currentThread();
for (int i = 0; i < 3; i++) {
System.out.println("子线程"+t.getName()+"输出:"+(i+1));
}
}
}
线程终止
- 当线程执行完成时,自动退出
- 使用变量来控制run方法退出的方式停止线程
守护线程
当所有的用户线程都退出时,守护线程自动退出
垃圾回收机制
public class Test {
public static void main(String[] args) {
//子线程设置为守护线程
myDaemonThread myDaemonThread = new myDaemonThread();
myDaemonThread.setDaemon(true);
myDaemonThread.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " 执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
/*
守护线程:
当用户线程退出后 子线程也自动退出
*/
class myDaemonThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " 正在执行");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
线程安全
概念
多个线程同时操作同一个共享资源的时候可能出现业务安全问题
模拟线程安全问题
package Thread_;
public class Demo5 {
public static void main(String[] args) {
Thread xiaoHong = new DrawThread("小红");
Thread xiaoMing = new DrawThread("小明");
xiaoMing.start();
xiaoHong.start();
}
}
class Account{
private static double moneys = 100000;
private Account(){
}
public static double getMoneys() {
return moneys;
}
public static void setMoneys(double moneys) {
Account.moneys = moneys;
}
public static boolean drawMoneys(double moneys){
String name = Thread.currentThread().getName();
if (moneys>Account.getMoneys()){
System.out.println(name+"来取钱,钱不够");
return false;
}
Account.moneys-=moneys;
System.out.println(name+"来取钱,取钱成功,剩余"+Account.moneys);
return true;
}
}
class DrawThread extends Thread{
public DrawThread(String name) {
super(name);
}
@Override
public void run() {
Account.drawMoneys(100000.0);
}
}
线程同步
认识线程同步
多个线程实现先后依次访问共享资源
**加锁:**每次只允许一个线程加锁,加锁之后才能访问,访问完毕之后自动解锁,然后其他线程才能再加锁继续
方法一:同步代码块
把访问共享资源的核心代码给上锁,保证线程安全
synchronized(同步锁){
访问共享资源的核心代码
}
对于当前同时执行的线程来说,同步锁必须是同一把(同一对象)
锁对象的选择:
- 实例对象:使用
this
- 静态对象:使用
类型.class
public class Demo5 {
public static void main(String[] args) throws InterruptedException {
Account acc1 = new Account(100000);
Thread xiaoHong = new DrawThread("小红",acc1);
Thread xiaoMing = new DrawThread("小明",acc1);
xiaoMing.start();
xiaoHong.start();
Account acc2 = new Account(100000);
Thread daGang = new DrawThread("大纲",acc2);
Thread daLi = new DrawThread("大力",acc2);
daGang.start();
daLi.start();
}
}
class Account {
private double moneys;
public Account() {
}
public Account(double moneys) {
this.moneys = moneys;
}
public double getMoneys() {
return moneys;
}
public void setMoneys(double moneys) {
this.moneys = moneys;
}
public void drawMoneys(double moneys) throws InterruptedException {
String name = Thread.currentThread().getName();
/*
* 两个人同时竞争lock这个对象(这把锁),只有一个人能够得到
* 上锁之后另外一个人要等待开锁
*
* 但是这个lock对于所有的对象是一个锁
* 一个对象上锁的时候 和该对象无关的对象也无法进入核心代码
* 非static建议使用 this
* static建议使用 ClassName.class
* */
synchronized (this) {
// Thread.sleep(5000); 测试
if (moneys > this.getMoneys()) {
System.out.println(name + "来取钱,钱不够");
} else {
this.moneys -= moneys;
System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);
}
}
}
}
class DrawThread extends Thread {
private Account acc;
public DrawThread(String name,Account acc) {
super(name);
this.acc = acc;
}
@Override
public void run() {
try {
acc.drawMoneys(100000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
方法二:同步方法
访问共享资源的核心方法给上锁
修饰符 synchronized 返回值类型 方法名称(形参列表){
操作共享资源的代码
}
public class Demo5 {
public static void main(String[] args) throws InterruptedException {
Account acc1 = new Account(100000);
Thread xiaoHong = new DrawThread("小红", acc1);
Thread xiaoMing = new DrawThread("小明", acc1);
xiaoMing.start();
xiaoHong.start();
Account acc2 = new Account(100000);
Thread daGang = new DrawThread("大纲", acc2);
Thread daLi = new DrawThread("大力", acc2);
daGang.start();
daLi.start();
}
}
class Account {
private double moneys;
public Account() {
}
public Account(double moneys) {
this.moneys = moneys;
}
public double getMoneys() {
return moneys;
}
public void setMoneys(double moneys) {
this.moneys = moneys;
}
/*
有一个隐含的锁 实例方法是 this 静态方法是 类型.class
*/
public synchronized void drawMoneys(double moneys) throws InterruptedException {
String name = Thread.currentThread().getName();
if (moneys > this.getMoneys()) {
System.out.println(name + "来取钱,钱不够");
} else {
this.moneys -= moneys;
System.out.println(name + "来取钱,取钱" + moneys + "成功,剩余" + this.moneys);
}
}
}
class DrawThread extends Thread {
private Account acc;
public DrawThread(String name, Account acc) {
super(name);
this.acc = acc;
}
@Override
public void run() {
try {
acc.drawMoneys(100000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
方法三:Lock锁
Lock锁是IDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大
Lock是接口,不能直接实例化,可以采用它的实现类**ReentrantLock
**来构建Lock锁对象。
package Thread_;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo5 {
public static void main(String[] args) throws InterruptedException {
Account acc1 = new Account(100000);
Thread xiaoHong = new DrawThread("小红", acc1);
Thread xiaoMing = new DrawThread("小明", acc1);
xiaoMing.start();
xiaoHong.start();
Account acc2 = new Account(100000);
Thread daGang =