Java多线程:
进程:一个进程对应一个程序,例如:在Windows中启动Word,开发环境下启动JVM
单核计算机同一个时间节点只能执行一个进程,双核对应一个时间点两个进程。
进程之间相互独立。
多进程,一边游戏,一边音乐(本质上是进程来回切换)
多进程不是提高执行速度,而是提高CPU使用率。
线程:线程是一个进程中的执行场景(客户端访问服务器)>一个进程对应多个线程
线程本质上也是来回切换的、间断的,只是计算机处理速度非常高。
多线程的作用:不是提高速度,而是提高应用程序使用率
线程之间共享“堆内存和方法区内存”栈内存是独立的“一个线程一个栈”
Java程序运行原理:
执行Java命令的时候,启动JVM ,开启进程,该进程会自动开启一个‘主线程’,这个‘主线程’会去某个类中调用main方法(所以main方法运行在‘主线程’中)。
Java中实现线程的方式
- 写一个类继承Thread类
- 写一个类实现runnable接口(推荐使用)
Thread中重要的两个方法:
Public void run() à 该方法是启动线程时自动调用
Public void start() 启动线程
线程生命周期:
线程优先级:
Java采用抢占CPU方式执行,优先级高的并不先执行,只是它获取时间片相对多一点。
最低1 最高10 默认5
通过setPriority()改变优先级
例:
package com.guanghui;
/*
* 1 Thread.sleep(毫秒)
* 2 sleep是一个静态方法,所以调用的时候类名.sleep()
* 3 该方法起到阻塞当前线程腾出CPU让给其他线程
*/
public class ThreadSleep {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Processor();
//给线程取名字
t1.setName("haha");
//开启线程
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
//这里的异常可以向上抛出,因为没有继承父类run方法
Thread.sleep(500);
}
}
}
class Processor extends Thread{
@Override
//重写run方法
//这里不能抛出异常,因为父类run方法中没有抛出异常
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+ "---->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();//让当前线程阻塞1s
}
}
}
}
终止线程随眠:
package com.guanghui;
/*
* 终止线程随眠,
注:可以调用interrupt()方法,该方法使用异常处理,不推荐使用
*/
public class ThreadSleep2 {
public static void main(String[] args) throws InterruptedException {
Processer p = new Processer();
Thread t = new Thread(p);
t.setName("t");
t.start();
Thread.sleep(5000);
p.run = false;
}
}
class Processer implements Runnable {
boolean run = true;
public void run() {
for (int i = 0; i < 10; i++) {
if (run) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out
.println(Thread.currentThread().getName() + "-->" + i);
} else
return;
}
}
}
线程同步(加锁)
同步编程模型:线程之间有序执行,下一个执行必须等待上一个线程执行完毕(银行取钱、幻读)
异步编程模型:线程之间独立执行,抢占cup执行
为什么引入线程同步?
为了数据安全,此时类似单线程。
什么情况使用线程同步
- 满足多线程环境
- 共享同一数据
- 共享数据涉及到数据的修改
案例:
此案例为线程异步(不安全)
package com.guanghui;
public class ThreadTest {
public static void main(String[] args) {
Account account = new Account("zhangsan", 5000);
Processer p = new Processer(account);
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
t1.start();
t2.start();
}
}
// 取钱线程
class Processer implements Runnable {
Account custmer;
public Processer(Account custmer) {
this.custmer = custmer;
}
public void run() {
custmer.getMoney(1000);
System.out.println("取款成功,余额:" + custmer.getBalance());
}
}
//Account类
public class Account {
private String actno;
private double balance;
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public Account() {
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public void getMoney(double money) {
double after = balance -money;
this.setBalance(after);
}
}
以下改进(线程同步),执行效率降低,但是数据安全。
public void getMoney(double money) {
//把需要同步的代码放入synchronize语句块中,this表示Account类的当前对象,当t1线程进入synchronize时会拿到对象锁直到把语句块执行完才归还,这个过程中其他线程进入synchronize拿不到对象锁不能执行该语句块。Synchronize关键字可以修饰成员方法,也表示当前对象锁
synchronized (this) {
double after = balance -money;
try {
//为了验证,随眠
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.setBalance(after);
}
}
Synchronize类锁案例
package com.guanghui;
/*
* synchronize可以修饰静态方法表示类锁类
*/
public class ThreadSynchdTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Demo01());
Thread t2 = new Thread(new Demo01());
t1.setName("t1");
t2.setName("t2");
t1.start();
//随眠,使t1线程优先执行
Thread.sleep(1000);
t2.start();
}
}
class Demo01 implements Runnable{
public void run() {
if("t1".equals(Thread.currentThread().getName())){
Demo02.m1();
}
if("t2".equals(Thread.currentThread().getName())){
Demo02.m2();
}
}
}
class Demo02
{
//此处synchronize修饰静态方法拿到类锁
public synchronized static void m1(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1 >>>>");
}
//由于类锁被m1拿到,所以此处必须等待m1方法执行完毕才能运行
public synchronized static void m2() {
System.out.println("m2 》》》》");
}
}
死锁
守护线程
线程分为用户线程和守护线程,一般来说守护线程是一个无限循环,它会等待用户线程结束才会跟着结束,例如JVM垃圾回收器就是一个守护线程,只有应用程序中所有线程结束,它才会结束。
定时器Timer
指定某一任务特定时间开始,间隔时间执行。
步骤:
- 创建定时器对象
- 指定定时任务(schedule方法)