多线程
1. 什么是进程?
一个程序的运行期间就是进程,多个程序运行及时多进程。
多进程有什么作用?
多进程的作用不是提高执行速度,而是提高CPU的使用率。进程与进程之间是独立的。
**什么是线程?**
线程是进程的一个执行场景,一个进程可以启动多个线程。
多线程有什么作用
多线程不是为了提高执行速度,而是提高应用程序的使用率。
线程和线程共享“堆内存和方法区内存”,栈内存是独立的,意思就是一个线程一个栈。
会给人类一个错觉,认为多个线程是在同时并发运行。
多线程的创建与使用
多线程创建与使用有两种方式。
多线程创建的第一种方式:
使用分为三步
- 定义线程,继承java.long.Thread类,重写run方法
- 创建线程,new 类,调用Thread中的start()方法
- 启动线程。
多线程创建的第二种方式
使用分为三步
- 定义线程,实行Runable接口,实现run方法
- 创建线程,调用Thread类中的构造方法,new Thread(Runable runable);调用start()方法
- 运行线程
上代码
线程的第一种闯将方式:
@java
public class ThreadTest01{
public static void main(String[] args){
Thread thread = new Realize01();
thread.start();
}
}
class Realize01 exteads Thread{
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("run-->" + i);
}
}
}
线程的第二种创建方式:
@java
public class ThreadTest02{
public static void main(String[] args){
Thread thread = new Thread(new Realize02);
thread.start();
}
}
class Realize02 implements Runable{
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("run-->" + i);
}
}
}
多线程的生命周期
- 新建:采用new语句创建完成
- 就绪:执行start()之后表示就绪状态,就绪状态的线程表示有权利争取CPU的时间片,谁先争取到时间片,谁就先运行。
- 运行:占用了CPU的时间片(就绪状态中线程或取到的时间片取决于你运行的时间,加入你在时间片的范围内没有把代码运行完,那么这个线程会重新到就绪状态,争取CPU的时间片,再次进入到运行状态中会接着上次没有执行完的代码接着执行),执行run方法。
- 阻塞:执行了wait语句、执行了sleep语句和等待某个对象锁,等待输入的场景
- 结束:run方法结束。
线程的调度与控制
Java虚拟机要负责线程的调度,取得CPU的使用权,目前有两种调度模式;分时调度模式:所有线程轮流是用CPU的使用权,平均分配每个线程占用CPU的时间片。 抢占试调度模式:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的CPU抢占的时间片会相对多一点。
线程的优先级:
线程的优先级分为三个
- 最大的为10(MAX_PRIORITY )
- 最小的为1(MIN_PRIORITY)
- 默认的为5(NORM_PRIORITY)
线程的优先级取决于线程的执行顺序
Thread.currentThread;获取线程当前的引用。
getName();获取当前的线程名称
setName();设置当前的线程名称
setPriority();设置线程优先级
public class ThreadTest02 {
public static void <u>main(String[] </u><u>args</u><u>)</u> {
Thread t1 = new Realize();
//设置本线程的线程名字
t1.setName("t1");
//设置线程的优先级
t1.setPriority(6);
Thread t2 = new Realize();
t2.setName("t2");
t2.setPriority(7);
t1.start();
t2.start();
}
}
class Realize extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-----" + i);
}
}
}
这行代码的运行结果t2的运行时间是大于t1的,也就是t2的时间片大于t1
线程的停止与中断
Thread.sleep(int time);在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
void join();等待该线程终止。在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行毕以后,该线程才继续执行下去.
Thread.yield;暂停当前正在执行的线程对象,并执行其他线程。
void interrupt();字面意思是中断的意思,但在java中,是通过某种方式去通知线程,而不是中断线程。
@java
public static void main(String[] args) throws Exception {
//字线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
});
t1.start();
// t1.join();//主线程的执行权让个t1
Thread.yield();//暂停主线程,执行t1线程
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println("Hello Word!");
}
}
这里运次结果是t1线程执行,然后每隔一秒执行一次主线程。加入不把join给注释掉,线程的执行权就会让给t1。
当线程在睡眠期间如何被中断
@java
public class ThreadTest06 {
public static void main(String[] args) throws Exception {
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10000000000000l);
System.out.println("Hello World!");
} catch (InterruptedException e) {
System.out.println("线程在睡眠中被吵醒了!");
e.printStackTrace();
}
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
}
});
t2.start();
Thread.sleep(5000);
t2.interrupt();
}
}
如何正确的终止线程
每次循环的时候都判断一下a,如果是true就睡眠,如果是false就执行输出语句
@java
public static void main(String[] args) throws Exception {
Realize07 t = new Realize07();
Thread tt = new Thread(t);
tt.start();
Thread.sleep(500);
t.a = false;
}
}
class Realize07 implements Runnable{
boolean a = true;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (a){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("线程被中断了!");
}
}
}
线程的同步和锁机制
1. 异步线程
- 两个线程同时执行,没有先后顺序,java一般都是异步执行,但是有时候在某些条件下,必须使用同步线 程。
2. 同步线程
- 同步线程就是t1线程执行完然后执行t2线程
- 执行条件就是必须在多线程环境下,两个线程共享同一块数据的时候,对这块数据进行删改操作。
上代码
@java
/*
* <u>定义一个账户,对这个账户进行取钱。
* 定义两个线程同时进行操作
*/
public class ThreadTest01 {
public static void main(String[] args) throws Exception{
Account act = new Account("account-01",5000);
Thread t1 = new Thread(new Realize(act));
Thread t2= new Thread(new Realize(act));
t1.start();
t2.start();
}
}
class Account{
private String name;//账户名
private double money;//账户余额
public Account(){
}
public Account(String name,double money){
this.name = name ;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
/**
* 对账户进行取钱
*/
public void drawmoney(double draw){
double nowmoney = this.money - draw;
this.setMoney(nowmoney);
}
}
class Realize implements Runnable{
Account act;
public Realize(){
}
public Realize(Account act){
this.act = act;
}
@Override
public void run() {
act.drawmoney(1000.0);
try{
Thread.sleep(1000);
}catch(Exception e){}
System.out.println(act.getName() + "取走1000元成功,余额:"+act.getMoney());
}
}
运行结果:
@java
account-01取走1000元成功,余额:4000.0
account-01取走1000元成功,余额:4000.0
Process finished with exit code 0
两个线程同时访问了一个操作,t1线程还没有执行完的时候,t2线程就已经获取到余额是4000。t2不会等t1执行完在去执行。加入取款机是这样的操作,这是违法的。需要等t1线程执行完再去执行t2线程,其实同步线程有很多种方法,效率最高的就是synchronized。
3. synchronized(锁对象){执行的要同步的代码}
这里对run方法中公共执行的代码进行了加锁,谁先找到对象锁,那个线程就先执行,这两个线程的数据是共享的,所以对象锁只有一个。
synchronized也可以当做加载方法中。
这个默认的也是this。
两者的区别:
- synchronized(锁对象){执行的要同步的代码}跟使用在方法中的效果一样,不过有些时候你无法保证你run方法里面的代码都是公共的,所以一般来说,synchronized(){}效率更高。
当你访问两个线程时,一个线程调用的方法加锁了,另一个线程调用的方法没有加锁,同时执行时,没有加锁的会不会受加锁的影响呢?
@java
public class ThreadTest02 {
public static void main(String[] args) throws Exception {
Realize02 rl = new Realize02();
T1 t = new T1(rl);
Thread t1 = new Thread(t);
t1.setName("t1");
Thread t2 = new Thread(t);
t2.setName("t2");
t1.start();
//保证T1先执行
Thread.sleep(1000);
t2.start();
}
}
class T1 implements Runnable{
Realize02 rl;
public T1(Realize02 rl) {
this.rl = rl;
}
public T1(){
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("t1")){
rl.m1();
}
if (Thread.currentThread().getName().equals("t2")){
rl.m2();
}
}
}
class Realize02{
public synchronized void m1(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1.....");
}
public void m2(){
System.out.println("m2.......");
}
}
运行结果:
@java
m2.......
m1.....
可以看到,尽管在t1线程执行完让主线程休眠1s之后在再执行t2线程,但是m2方法并没有等m1方法休眠过后再执行,而是直接执行。
加入m2方法也加synchronize,那么m2会等m1先执行完后才执行,因为两个线程使用的是一个对象锁。谁先拿到谁就先执行。
@java
public synchronized void m2(){
System.out.println("m2.......");
}
如果两个线程使用的不是同一个对象锁,那么m2方法同样不会受到m1方法的影响,在t2拿到就直接执行。
类锁
- 假如你要添加的锁的方法不是成员方法,而是静态方法,那么这个锁指向的就不是对象锁而是 类锁。
- 类锁只有一个。
@java
public class ThreadTest02 {
public static void main(String[] args) throws Exception {
Realize02 rl1 = new Realize02();
Realize02 rl2 = new Realize02();
T1 t = new T1(rl1);
T1 tt = new T1(rl2);
Thread t1 = new Thread(t);
t1.setName("t1");
Thread t2 = new Thread(tt);
t2.setName("t2");
t1.start();
//保证T1先执行
Thread.sleep(1000);
t2.start();
}
}
class T1 implements Runnable{
Realize02 rl;
public T1(Realize02 rl) {
this.rl = rl;
}
public T1(){
}
@Override
public void run() {
if (Thread.currentThread().getName().equals("t1")){
<u>rl</u><u>.</u><u>m1</u><u>()</u>;
}
if (Thread.currentThread().getName().equals("t2")){
<u>rl</u><u>.</u><u>m2</u><u>()</u>;
}
}
}
class Realize02{
public synchronized static void <u>m1()</u>{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1.....");
}
/**
*在m2也添加方法锁synchronize会等到m1执行完之后才执行,因为两个对象使用的是同一个对象锁
*/
public synchronized static void <u>m2()</u>{
System.out.println("m2.......");
}
}
运行结果是,m2先等m1执行完然后才执行,尽管两个使用的不是同一个对象。原因就是因为不管你是用的是不是同一个对象,只要是这个类的,那么类锁就只有一个,谁先拿到就执行谁。
死锁
当线程A锁住a的同时又想锁住b,线程B想锁住b的同时又想锁住a,这样“你要拿我,我要拿你就叫做死锁”
@java
public class ThreadTest03 {
public static void <u>main(String[] </u><u>args</u><u>)</u> {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Thread(new T2(o1,o2));
Thread t2 = new Thread(new T3(o1,o2));
t1.start();
t2.start();
}
}
class <u>T2</u> implements Runnable{
Object o1;
Object o2;
public T2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
}
}
}
}
class T3 implements Runnable{
Object o1;
Object o2;
public T3(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
t2线程先锁住o1然后去锁o2,t3线程先锁住o2然后去锁o1。o1,o2为两个人的共享数据。
守护线程
java线程分为守护线程和非守护线程。非守护线程又叫做用户线程,我们平时编写的一些线程就叫做用户线程。
守护线程会在所有的用户线程结束才会自动结束。
Java在运行的时候调动JVM,JVM是一个进程,里面有多个线程,JC(垃圾回收器)就是守护线程,它会在所有的用户进程结束完然后自动退出程序。
java吧线程设置守护线程的方法:在start之前调用strDaemon(true)方法。
- setDaemon(true) 必须在 start() 之前设置,否则会抛出IllegalThreadStateException异常,该线程仍默认为用户线程,继续执行
- 守护线程创建的线程也是守护线程
- 守护线程不应该访问、写入持久化资源,如文件、数据库,因为它会在任何时间被停止,导致资源未释放、数据写入中断等问题
@java
public class TreadTest01 {
public static void main(String[] args) {
Thread t1 = new Thread(new on());
//设置该线程为守护线程
t1.setDaemon(true);
t1.start();
//主线程需要执行的代码
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() +
"----" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class on implements Runnable{
@Override
public void run() {
int i = 0;
while(true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"----"+i);
i++;
}
}
}
运行结果
main----0
main----1
main----2
Thread-0----0
main----3
Thread-0----1
main----4
main----5
Thread-0----2
main----6
main----7
Thread-0----3
main----8
main----9
Thread-0----4
Process finished with exit code 0
尽管t线程里面的执行的是死循环,但是使用了setDaeon()方法转换为守护线程之后,在所有的用户线程执行完之后,自动break(结束)。
定时器(Timer类)
Timer类的作用是设置计划任务,但是任务封装的类是TimerTask类。执行计划任务的代码要放入TimerTask的子类中,因此TimerTask是一个抽象类。
schedule(TimerTask task, Date time)方法 在指定的日期执行某一件事
@java
class Task02 extends <u>TimerTask</u>{
@Override
public void <u>run()</u> {
System.out.println("Just I miss you!");
}
}
实现类
@java
import java.text.SimpleDateFormat;
import java.util.Timer;
import java.util.TimerTask;
public class <u>TimerTest02</u> {
public static void main(String[] args) throws Exception {
Timer tmr = new Timer();
tmr.schedule(new <u>Task02</u>(), new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss").parse("2020-10-6 13:04:00"));
}
}
运行结果
@java
Just I miss you!
Process finished with exit code -1
schedule(TimerTask task,Date time,long period);激活从什么时间开始,每隔多长时间,执行一次任务
@java
class Task extends <u>TimerTask</u> {
@Override
public void <u>run()</u> {
System.out.println(Calendar.getInstance().getTime().toLocaleString());
}
}
测试类
@java
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;
public class <u>TimerTest01</u> {
public static void main(String[] args) throws Exception {
//1.创建定时器
Timer tmr = new Timer();
tmr.schedule(new <u>Task</u>(),new SimpleDateFormat("yyyy-MM-dd
HH:mm:ss").parse("2020-10-6 13:07:00"),10*1000);
}
}
运行结果:每隔10秒钟就输出一次时间!
@java
2020-10-6 13:07:02
2020-10-6 13:07:12
2020-10-6 13:07:22
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。