多线程:
多线程的原理:
原理:多线程执行时候,在栈内存中,其实每一个执行线程都有一片所属的栈内存的空间,进行方法的压栈,和弹栈.
原理图:
多线程的创建:
三种方式:
第一种继承:
1 package com.heima.duoxiancheng;
2
3 //多线程的继承方式创建
4 public class Demo01 extends Thread {
5 //利用继承的特点
6 //将线程名称传递进行设置
7 public Demo01(String name) {
8 super(name);
9 }
10
11 //重写run方法
12 @Override
13 public void run() {
14 for (int i = 0; i < 5; i++) {
15 //getName()方法来自与父亲类Thread
16 System.out.println(getName() + "正在执行:" + i);
17 }
18 }
19
20 public static void main(String[] args) {
21 //创建类对象
22 Demo01 a = new Demo01("德玛");
23 //创建线程对象
24 Thread B = new Thread(a);
25 //开启线程
26 B.start();
27 //在主线程种执行
28 for (int j = 0; j < 5; j++) {
29 System.out.println("我叫做:" + j);
30 }
31 }
32 }
第二种接口:
1 package com.heima.duoxiancheng; 2 //接口方式书写多线程 3 public class Demo02 implements Runnable { 4 //重写run方法 5 @Override 6 public void run() { 7 for (int i = 0; i < 5; i++) { 8 System.out.println(Thread.currentThread().getName() + "-" + i); 9 } 10 } 11 public static void main(String[] args) { 12 //创建实现类对象 13 Demo02 a=new Demo02(); 14 //创建线程对象 15 Thread thread = new Thread(a,"剑圣"); 16 //开启线程 17 thread.start(); 18 for (int j = 0; j < 5; j++) { 19 System.out.println("我是主线程:"+j); 20 } 21 22 } 23 }
三.匿名内部类方式创建
1 package com.heima.duoxiancheng;
2 //匿名内部类
3 public class Demo03 {
4 public static void main(String[] args) {
5 //创建Runnable对象
6 Runnable runnable = new Runnable() {
7 //重写run方法
8 @Override
9 public void run() {
10 //创建多态对象
11 for (int i = 0; i < 5; i++) {
12 System.out.println("德玛:" + i);
13 }
14 }
15 };
16 new Thread(runnable).start();
17 for (int j = 0; j < 5; j++) {
18 System.out.println(Thread.currentThread().getName()+" "+j);
19 }
20 }
21 }
优势:使用线程的匿名内部类方式,可以方便的实现每个线程执行不同的线程任务.
Thread和Runnable的区别:
总结:
实现Runnable接口比继承Thread类所具有的优势:
-
适合多个相同的程序代码的线程去共享同一个资源。
-
可以避免java中的单继承的局限性。
-
增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
-
线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类.
扩充:在Java种每次程序运行至少启动两个线程,一个是main线程,一个是JVM.
Thread的构造方法:
常用方法: public String getName() :获取当前线程名称。public static Thread currentThread() :返回对当前正在执行的线程对象的引用。public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。public void run() :此线程要执行的任务在此处定义代码。public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。注意:如果一个类继承Thread,则不适合资源的共享,但是实现了Runable接口的话,则很容易的实现资源共享.
|
实现Runnable接口比继承Threed类所具有的优势
1.适应多个相同的程序代码的线程去共享一个资源.
2.可以避免java中的单继承的局限性.
3.增加程序的健壮性,实现解耦操作,任务可以被多个线程共享,任务和线程独立.
4.线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类
耦合性:类和类之间的相互影响的程度.
扩充:在java中,每次程序运行至少启动2个线程,一个是main线程,一个是垃圾收集线程,
因为每当使用Java命令执行一个类的时候,一个是垃圾收集线程,因为每当使用Java执行一个类
实际东辉启动一个jvm,每个JVM其实都是在操作系统启动一个进程.
|
线程安全:
定义:
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
|
实例题目:
|
代码演示:
1 package com.heima.duoxiancheng;
2 //通过接口方式
3 public class Demo04 implements Runnable{
4 private int ticket=100;//设置票的个数
5 Object jock=new Object();
6 //重写run方法
7 //执行买票操作
8 @Override
9 public void run() {
10 //窗口永远在开启
11 while (true){
12 //使用同步代码块解决去重问题
13 synchronized (jock){
14 if(ticket>0) {//有票可以卖
15 try {
16 Thread.sleep(100);//模拟出票的时间
17 } catch (InterruptedException e) {
18 e.printStackTrace();
19 }
20 //获取当前线程对象的名字
21 String name = Thread.currentThread().getName();
22 System.out.println(name + "正在卖:" + ticket-- + "张。");
23 }
24 }
25 }
26 }
27
28 public static void main(String[] args) {
29 //创建线程任务的对象
30 Demo04 thread = new Demo04();
31 //创建窗口的对象
32 Thread thread1 = new Thread(thread,"窗口1");
33 Thread thread2 = new Thread(thread,"窗口2");
34 Thread thread3 = new Thread(thread,"窗口3");
35 thread1.start();
36 thread2.start();
37 thread3.start();
38 }
39 }
线程的同步:
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题
解决:
解决上诉问题多线程访问一个资源安全性问题,也就是解决重复的问题,java中提供了(synchronized)来解决
线程访问使用三种方式完成同步操作
1.同步代码快(synchronized)关键字可以用于方法的某个区块中表示区块资源的互斥访问.
格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标志记了一个锁
.锁对象(可以是任意数据对象)
.多个线程对象,要使用同一把锁.
注意:在任何时候,最多仍许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLDCKED).
2.同步方法
3.锁机制
同步方法:
同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁:
对于非static方法,同步锁就是this.
对于static方法,我们使用当前方法所有类的字节代码对象(类名.class)
代码演示:
1 package com.heima.biji;
2 //同步方法
3 public class Mythread04 implements Runnable{
4 private int ticket = 100;//定义电影票的个数
5 //重写run方法
6 @Override
7 public void run() {
8 //每个窗口买票的过程
9 //窗口永远在开启
10 while (true){
11 sellTicket();
12 }
13 }
14 public synchronized void sellTicket(){
15 if(ticket>0){
16 try {
17 Thread.sleep(100);
18 } catch (InterruptedException e) {
19 e.printStackTrace();
20 }
21 //获取当前线程对象的名字
22 String name = Thread.currentThread().getName();
23 System.out.println(name + "正在买:" + ticket-- + "张");
24 }
25 }
26 }
lock锁:
java.util.concurrent.loks.Lock机制提供了比synchronized代码块,和synchronized方法
Lock锁也称同步锁,加锁与释放锁方法化了,如下: public void lock() :加同步锁。 lock他是一个接口,他只能new 实现类ReentrantLock
public void unlock() :释放同步锁。 |
代码快:
1 package com.heima.biji;
2 import java.lang.invoke.VarHandle;
3 import java.util.concurrent.locks.Lock;
4 import java.util.concurrent.locks.ReentrantLock;
5
6 //Lock锁
7 public class Mythread05 implements Runnable {
8 //设置票数
9 private int ticket = 100;
10 Lock lock = new ReentrantLock();
11
12 //执行买票操作
13 @Override
14 public void run() {
15 while (true) {
16 lock.lock();//加同步锁
17 if (ticket > 0) {//有票可以买
18 //每个窗口买票的操作
19 //窗口 永远开启
20 try {
21 Thread.sleep(100);
22 } catch (InterruptedException e) {
23 e.printStackTrace();
24 }
25 //获取当前线程对象的名字
26 String name = Thread.currentThread().getName();
27 System.out.println(name + "正在买:" + ticket-- + "票");
28 }
29 lock.unlock();//释放同步锁
30 }
31 }
32 }
线程状态概述:
当线程被创建并启动后,他既不是一启就进入执行状态,也不是一直处于执行状态:
在线程生命周期中有6种状态.
线程状态
|
导致方式条件
|
NEW(新建)
|
线程刚被创建,但是并未启动。还没调用start方法。
|
Runnable(可运行)
|
线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
|
Blocked(锁阻塞)
|
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
|
Waiting(无限等待)
|
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。通过wait()方法进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
|
TimedWaiting(计时等待)
|
同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
|
Teminated(被终止) : 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
- Timed Waiting在API中的描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态。单独的去理解这句话,真是玄之又玄,其实我们在之前的操作中已经接触过这个状态了,在哪里呢?