java学习day18~19---多线程
一.线程和进程(单核)
进程:CPU中有多个程序“同时”在使用,CPU在一个时间点上只能执行一件事情,而CPU分片段执行不同程序,
但是切换速率十分快,给人感觉是在同时使用
进程的作用不是提高执行速度,而是提高CPU的使用率
线程:一个进程中的执行场景,一个进程有多个线程,如淘宝抢购,都在使用百度搜索
线程的作用不是提高执行速度,而是提高应用程序的使用率
线程的存在:
线程之间的堆内存和方法区内存是共享的,但不同线程都是不同的栈内存,
二.线程的创建和启动
方式1:子类继承Thread,重写里面的run方法,调用start方法启动(也可以不用子类,直接new一个Thread,用匿名内部类的方式)
注意:run方法不能抛出异常
public class ThreadWork
{
public static void main(String[] args) {
Thread t = new Processer();
t.start(); //这个代码告诉JVM给t线程分配一个新的栈,启动之后自动调用run方法
for(int i = 0; i < 100; i++) {
System.out.println("main---" + i);
}
}
}
class Processer extends Thread
{
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("thread---" + i);
}
}
}
//匿名内部类的方式
public class ThreadWork
{
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("thread---" + i);
}
}
};
t.start();
for(int i = 0; i < 100; i++) {
System.out.println("main---" + i);
}
}
}
//拉姆达表达式简化后
public class ThreadWork
{
public static void main(String[] args) {
new Thread(() -> {
for(int i = 0; i < 100; i++) {
System.out.println("thread---" + i);
}
}).start();
for(int i = 0; i < 100; i++) {
System.out.println("main---" + i);
}
}
}
//运行结果:两个循环随机交互执行
//结论:main方法结束之后只是主线程结束,但是其他线程还有栈帧,所以main方法结束,程序可能还在运行
//注意:如果只调用run方法,那么这就只是普通的方法调用,整个程序只有一个主线程,等到run方法执行结束之后,才会执行main方法中的循环
方式2:子类继承Runnable接口,实现里面的run方法(推荐使用)
public class ThreadWork
{
public static void main(String[] args) {
Thread t = new Thread(new Processer()); //Thread t = new Thread(Runnable接口);
t.start(); //这个代码告诉JVM给t线程分配一个新的栈,启动之后自动调用run方法
for(int i = 0; i < 100; i++) {
System.out.println("main---" + i);
}
}
}
class Processer implements Runnable
{
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println("thread---" + i);
}
}
}
三.线程的生命周期
进入就绪状态之后,线程有权利去抢夺CPU时间片,这个时间片就是执行权,抢到之后就可以进入运行状态,当时间
用完而运行还没有结束之后,就会继续返回到就绪状态,继续抢夺时间片,然后继续接着执行run方法,直至方法执行完毕
用第一个创建线程的例子来讲,得到的结果是两个循环交替输出,这就是因为两个线程抢夺时间片,先抢到的先执行,
抢到的时间不够返回来继续抢夺执行,直至线程结束
四.常用的方法
getName(); 获取线程名字
currentThread(); 获取当前的线程
sleep(); 让当前线程休眠一段指定时间,阻塞线程,让CPU腾出时间片,让其他线程执行,是一个静态方法
getPriority(); 获取线程优先级
join(); 等待某个线程执行结束
interrupt() 可以打断正在等待的线程(包括sleep, join的等待)
yield() 让位,给同一个优先级的线程让位,但是让位时间不固定,也是静态方法
不推荐使用的方法
stop() 让线程停止
suspend() 让线程暂停
resume() 让线程继续
sleep()详解
public class ThreadWork
{
public static void main(String[] args) throws Exception{
Thread t = new Thread(() -> {
for(int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
});
t.setName("t1"); //将线程命名为t1
t.start(); //让线程运行
t.sleep(5000); //此时调用sleep方法会让t线程休眠5秒吗
System.out.println("hello");
}
}
解析:不会,因为sleep是静态方法,与对象无关,t.start()相当于是Thread.start(), 而Thread当前是主线程
所以t线程不会休眠,而是主线程休眠
//interrupt()详解
public class ThreadWork
{
public static void main(String[] args) throws Exception{
Thread t = new Thread(() -> {
try {
Thread.sleep(1000000000000L); //很长时间段的休眠
}
catch (Exception e) {
e.printStackTrace();
}
for(int i = 0; i < 30; i++) {
System.out.println("thread" + i);
}
});
t.start(); //让线程运行
Thread.sleep(5000); //主线程休眠五秒
t.interrupt(); //五秒后打断t线程的休眠
System.out.println("hello");
}
}
//结果:五秒后t线程执行语句,因为休眠被打断
五.线程同步
同步编程模型:t1和t2线程必须等待一个执行完毕之后,再执行另一个
异步编程模型:t1和t2线程各自执行各自的,谁也不等谁引入线程同步的原因:为了数据安全
使用线程同步的条件:
1.多线程环境 2.共享一个数据 3.共享的数据涉及到修改操作
public class ThreadWork
{
public static void main(String[] args) {
Account a = new Account("张三", 5000);
Thread t1 = new Thread(new Processor(a));
Thread t2 = new Thread(new Processor(a));
t1.start();
t2.start();
}
}
class Processor implements Runnable
{
Account a;
public Processor(Account a) {
this.a = a;
}
public void run() {
a.withdraw(200);
System.out.println("取了200,还剩" + a.getBalance());
}
}
//账户类
class Account
{
private String actno;
private int balance;
public Account(String actno, int balance) {
this.actno = actno;
this.balance = balance;
}
public void setActno(String actno) {
this.actno = actno;
}
public String getActno() {
return actno;
}
public void setBalance(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
//提供一个取款方法
public void withdraw(int money) {
int after = balance - money;
this.setBalance(after);
}
}
//运行结果:随机输出4600,4800
//因为两个线程交互了,一个取款完毕之后,还没有更新,第二个有可能也已经执行,所以余额有问题
此时需要两个线程分别执行,引入线程同步
//其他代码不变,修改取款操作
public void withdraw(int money) {
synchronized(this) { //用锁将取款的具体操作锁住
try{
Thread.sleep(1000);
} catch(Exception e) {
}
int after = balance - money;
this.setBalance(after);
}
}
用synchronized(this)锁住,那么程序执行的时候,就回去寻找this对象锁,找到则执行,执行完毕归还对象锁,否则等待,直到获取对象锁
六.类锁
关于类锁:类锁只有一把,且与对象无关
虽然t1和t2是两个不同的线程,但是公用一个myClass对象,而该类中的方法用的是类锁,类锁只有一把,与对象没有关系
public class ThreadWork {
public static void main(String[] args) throws Exception {
myClass mc = new myClass();
Processor p1 = new Processor(mc);
Processor p2 = new Processor(mc);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(p2);
t1.setName("t1");
t2.setName("t2");
t1.start();
Thread.sleep(1000); //为了保证t1线程先执行
t2.start();
}
}
class Processor implements Runnable {
myClass mc;
Processor(myClass mc) {
this.mc = mc;
}
public void run() {
if(Thread.currentThread().getName().equals("t1")) {
mc.m1();
}
if(Thread.currentThread().getName().equals("t1")) {
mc.m2();
}
}
}
class myClass
{
public synchronized static void m1() {
try{
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("m1...");
}
public synchronized static void m2() {
System.out.println("m2...");
}
}
七.死锁
两个线程互相锁住,如两个线程t1,t2,两个对象o1,o2,t1线程先锁住o1对象,然后锁o2对象,t2线程先锁o2对象,再锁o1对象,但是因为两个线程谁都不肯先放,所以进入了僵持状态,形成死锁
public class ThreadWork
{
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Thread(new T1(o1, o2));
Thread t2 = new Thread(new T2(o1, o2));
t1.start();
t2.start();
}
}
class T1 implements Runnable
{
Object o1;
Object o2;
T1(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized(o1) {
try {
Thread.sleep(1000);
} catch(Exception e) {
e.printStackTrace();
}
synchronized(o2) {}
}
}
}
class T2 implements Runnable
{
Object o1;
Object o2;
T2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
public void run() {
synchronized(o2) {
try {
Thread.sleep(1000);
} catch(Exception e) {
e.printStackTrace();
}
synchronized(o1) {}
}
}
}
上一篇: 【Leetcode2】两数相加