多线程
一、线程的概念
线程:在一个程序中同时运行的多个独立流程,每一个独立的流程就是一个线程
进程:线程是程序执行的最小单位,一个进程是由一个或多个线程组成,进程之间相互独立。一个程序的执行就是进程。
并发:一个处理器同时处理多个任务,
并行:多个处理器同时处理多个任务。
同步:当程序1调用程序2时,得先把程序1中所有的步骤都进行完,再执行一个程序
异步:当程序1调用程序2时,程序中的步骤只要地运行就可以。不用管哪个程序中的步骤先进行完。
线程状态:新建-可运行-运行-阻塞-死亡
通知机制:wait() 和 notify() 实现等待通知机制;
主线程:当JVM启动之后,加载类文件,发现main方法,那么就会为main方法创建一个线程,用于main方法执行,这个为main方法创建的线程称为主线程
封装:把属性和行为封装在类中
继承:子类可继承父类的属性和方法
多态:同一父类的,不同子类的对象,可有不同的行为。
二、Java中线程的使用
在Java中创建线程的方法有两种: 方法一: 继承java.lang.Thread类 方法二: 实现java.lang.Runnable接口
继承Thread类
- 自定义一个线程类继承自Thread
- 重写Thread的run方法
- 创建一个该类的对象
- 调用该类继承自Thread的start方法开启线程
创建线程①
继承Thread类创建线程类:
public class MyTheardB extends Thread{
@Override
public void run() {
for(char c = 'A'; c<='Z';c++){
System.out.println(c);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TEstMain {
public static void main(String[] args) throws Exception {
Thread a = new MythreadA();
a.start();
for (int i = 1; i <= 26; i++) {
System.out.println(i);
Thread.sleep(1000);
}
}
}
Runnable接口开发线程
- 用户开发一个类实现Runnable接口
- 实现run()
- 运行线程
创建线程②
实现Runnable创建线程类:
public class MyThreadD implements Runnable{
@Override
public void run() {
for (char i = 'a'; i < 'z'; i++) {
System.out.println(i+"@@@");
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
测试类:
public class TEstMain {
public static void main(String[] args) throws Exception {
for (int i = 1; i <= 26; i++) {
System.out.println(i);
Thread.sleep(1000);
}
}
}
根据学习过的知识判断,以上两种线程创建方法,哪一种更好 ? Runnable 可实现多继承。
程序的输出结果固定吗?不固定
程序中存在几个线程?执行的先后顺序 :顺序:主线程,其余的先后顺序不一定。
可不可以直接在main方法中直接调用run():可以,只是单纯的对象调用方法。
三、线程的状态

sleep()
void sleep(long time)方法用于使当前线程休眠指定的毫秒数
一旦调用了sleep方法,该线程就由运行状态进入了阻塞状态

join()
利用sleep方法对线程的控制是非常不精确的 join方法可以精确控制线程 join方法也会导致线程阻塞
特点:如果当前线程中调用了另外一个线程的 join方法,当前线程会立即阻塞,直到另外一个线程运行完成
练习(实践)
创建一个线程,满足设定的条件以后,将另一个线程添加进来,实现。
继承Thread类创建A类:
public class Thread_A extends Thread{
private Thread b;
public Thread getB() {
return b;
}
public void setB(Thread b) {
this.b = b;
}
public Thread_A() {
}
public Thread_A(Thread b) {
this.b = b;
}
@Override
public void run() {
for (char i = 'a'; i < 'z'; i++) {
System.out.println(i);
if(i == 'c'){
try {
b.join(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
继承Thread创建B类:
public class Thread_B extends Thread{
@Override
public void run() {
for (int i = 1; i <= 26; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类:
public class TMain {
public static void main(String[] args) {
Thread_A a = new Thread_A();
Thread_B b = new Thread_B();
a.setB(b);
a.start();
b.start();
}
}
运行结果: 
其他常用方法:
public class TMain {
public static void main(String[] args) {
Thread_A a = new Thread_A();
Thread_B b = new Thread_B();
a.setB(b);
a.setName("线程——A");
String name = a.getName();
a.getId();
int priority = a.getPriority();
a.setPriority(1);
a.setPriority(Thread.MAX_PRIORITY);
a.start();
b.start();
}
}
Thread.currentThread().getName()
获取当前的线程名称。
四、线程同步问题
synchronized 同步
产生数据不一致的原因 多个线程并发访问了同一个对象,如果破坏了不可分割的操作,从而就会造成数据不一致
被多线程并发访问时如果一个对象有可能出现数据不一致的问题,那么这个对象称为线程不安全的对象
如何解决多线程并发访问的问题
synchronized(object){
代码块
}
public synchronized void deposit(int money){
this.balance += money;
System.out.println(Thread.currentThread().getName() + "存储了" + money + " 余额为:" + this.balance);
}
客户存取款多线程实例:
Account类:(包含存取款方法,同步)
package com.richanglianxi_suihsou.one.Thread_1.Balance;
public class Person {
private int balance;
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public Person() {
}
public Person(int balance) {
this.balance = balance;
}
public synchronized void desvion(int money){
this.balance += money;
System.out.println(Thread.currentThread().getName()+"您存储了:"+money+" 账户余额为:"+this.balance);
}
public void withdraw(int money) {
synchronized (this) {
this.balance -= money;
System.out.println(Thread.currentThread().getName()+"您取了:" + money + " 账户余额为:" + this.balance);
}
}
}
继承Thread类创建线程A:
package com.richanglianxi_suihsou.one.Thread_1.Balance;
public class Per_B implements Runnable {
private Person p;
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
public Per_B(Person p) {
this.p = p;
}
public Per_B() {
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
p.withdraw(200);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Runnable接口创建线程B:
package com.XC_Theard.CunQuPay;
public class Person_B extends Thread{
private Account acc;
public Account getAcc() {
return acc;
}
public void setAcc(Account acc) {
this.acc = acc;
}
public Person_B() {
}
public Person_B(Account acc) {
this.acc = acc;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
acc.withdraw(100);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类:
package com.richanglianxi_suihsou.one.Thread_1.Balance;
public class Main {
public static void main(String[] args) {
Person p = new Person(1000);
Thread a = new Per_A(p);
Runnable bb = new Per_B(p);
Thread b = new Thread(bb);
a.setName("线程A:");
b.setName("线程B:");
a.start();
b.start();
}
}
运行结果:
线程A:您存储了:1000 账户余额为:2000 线程B:您取了:200 账户余额为:1800 线程A:您存储了:1000 账户余额为:2800 线程B:您取了:200 账户余额为:2600 线程B:您取了:200 账户余额为:2400 线程A:您存储了:1000 账户余额为:3400
Process finished with exit code 0
Thread.currentThread().getName() 获取当前的线程名称。
五、 notify方法和wait方法
死锁:
线程A:
@Override
public void run() {
synchronized(a){
System.out.println(Thread.currentThread().getName()+"获取a锁!");
synchronized(b){
System.out.println(Thread.currentThread().getName()+"获取b锁!");
}
System.out.println(Thread.currentThread().getName()+"释放b锁");
}
System.out.println(Thread.currentThread().getName()+"释放a锁");
}
线程B:
@Override
public void run() {
synchronized(b){
System.out.println(Thread.currentThread().getName()+"获取b锁!");
synchronized(a){
System.out.println(Thread.currentThread().getName()+"获取a锁!");
}
System.out.println(Thread.currentThread().getName()+"释放a锁");
}
System.out.println(Thread.currentThread().getName()+"释放b锁");
}
运行结果:
线程A:获取a锁! 线程B:获取b锁!
解锁:(wait())
wait() 释放该锁//当前线程等待(也可以在括号里加等待时间)。
notify() 唤醒等待的单个线程;
notifyAll() 唤醒等待的所有线程;
创建一个AA类:
package com.XC_Theard.SiLuck;
public class Thrad_AA extends Thread{
private Object a;
private Object b;
public Object getA() {
return a;
}
public void setA(Object a) {
this.a = a;
}
public Object getB() {
return b;
}
public void setB(Object b) {
this.b = b;
}
public Thrad_AA() {
}
public Thrad_AA(Object a, Object b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized(a){
System.out.println(Thread.currentThread().getName()+"获取a锁!");
try {
a.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(b){
System.out.println(Thread.currentThread().getName()+"获取b锁!");
}
System.out.println(Thread.currentThread().getName()+"释放b锁");
}
System.out.println(Thread.currentThread().getName()+"释放a锁");
}
}
创建一个BB类线程:
package com.XC_Theard.SiLuck;
public class Thread_BB extends Thread{
private Object a;
private Object b;
public Object getA() {
return a;
}
public void setA(Object a) {
this.a = a;
}
public Object getB() {
return b;
}
public void setB(Object b) {
this.b = b;
}
public Thread_BB() {
}
public Thread_BB(Object a, Object b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
synchronized(b){
System.out.println(Thread.currentThread().getName()+"获取b锁!");
synchronized(a){
System.out.println(Thread.currentThread().getName()+"获取a锁!");
a.notify();
}
System.out.println(Thread.currentThread().getName()+"释放a锁");
}
System.out.println(Thread.currentThread().getName()+"释放b锁");
}
}
测试函数:
package com.XC_Theard.SiLuck;
public class Main {
public static void main(String[] args) {
Object s = new Object();
Object s1 = new Object();
Thread a = new Thrad_AA(s, s1);
Thread b = new Thread_BB(s, s1);
a.setName("线程A:");
b.setName("线程B:");
a.start();
b.start();
}
}
运行结果:
线程B:获取b锁! 线程A:获取a锁! 线程B:获取a锁! 线程B:释放a锁 线程B:释放b锁 线程A:获取b锁! 线程A:释放b锁 线程A:释放a锁
Process finished with exit code 0
死锁的产生原因和解决方法:
是由于两个或以上的线程互相持有对方需要的资源,导致这些线程处于等待状态,无法执行。
用wait() 和 notifyall();
练习题:生产者生产食物,消费者拿走食物,利用解决死锁解决问题。
编号类:
package com.XC_Theard.ShengChanXiaoFei;
public class ProDuct {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ProDuct() {
}
public ProDuct(int id) {
this.id = id;
}
@Override
public String toString() {
return "产品:"+id;
}
}
生产者:
package com.XC_Theard.ShengChanXiaoFei;
import java.util.Random;
public class Perductor extends Thread{
private Storage sto;
public Storage getSto() {
return sto;
}
public void setSto(Storage sto) {
this.sto = sto;
}
public Perductor() {
}
public Perductor(Storage sto) {
this.sto = sto;
}
@Override
public void run() {
for (int i = 1; i < 20; i++) {
ProDuct p = new ProDuct(i);
sto.push(p);
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者:
package com.XC_Theard.ShengChanXiaoFei;
import java.util.Random;
public class Consumer extends Thread{
private Storage sto;
public Storage getSto() {
return sto;
}
public void setSto(Storage sto) {
this.sto = sto;
}
public Consumer() {
}
public Consumer(Storage sto) {
this.sto = sto;
}
@Override
public void run() {
for (int i = 1; i < 20; i++) {
sto.pop();
try {
Thread.sleep(new Random().nextInt(3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
存储和取出方法:
package com.XC_Theard.ShengChanXiaoFei;
public class Storage {
private ProDuct[] peos = new ProDuct[5];
int index = 0;
public synchronized void push(ProDuct p) {
while(index >= peos.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
peos[index] = p;
index++;
System.out.println(Thread.currentThread().getName()+"在"+index+"位置放入了"+p);
this.notifyAll();
}
public synchronized void pop() {
while(index<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index--;
ProDuct p = peos[index];
System.out.println(Thread.currentThread().getName()+"在"+index+"位置取出了"+p);
this.notifyAll();
}
}
测试函数:
package com.XC_Theard.ShengChanXiaoFei;
public class Maina {
public static void main(String[] args) {
Storage s =new Storage();
Perductor p = new Perductor(s);
Consumer c = new Consumer(s);
p.setName("生产者:");
c.setName("消费者:");
p.start();
c.start();
}
}
运行结果:
生产者:在1位置放入了产品:1 消费者:在0位置取出了产品:1 生产者:在1位置放入了产品:2 生产者:在2位置放入了产品:3 生产者:在3位置放入了产品:4 生产者:在4位置放入了产品:5 消费者:在3位置取出了产品:5 生产者:在4位置放入了产品:6 生产者:在5位置放入了产品:7 消费者:在4位置取出了产品:7 生产者:在5位置放入了产品:8 消费者:在4位置取出了产品:8 生产者:在5位置放入了产品:9 消费者:在4位置取出了产品:9 生产者:在5位置放入了产品:10 消费者:在4位置取出了产品:10 生产者:在5位置放入了产品:11 消费者:在4位置取出了产品:11 生产者:在5位置放入了产品:12 消费者:在4位置取出了产品:12 生产者:在5位置放入了产品:13 消费者:在4位置取出了产品:13 生产者:在5位置放入了产品:14 消费者:在4位置取出了产品:14 生产者:在5位置放入了产品:15 消费者:在4位置取出了产品:15 生产者:在5位置放入了产品:16 消费者:在4位置取出了产品:16 生产者:在5位置放入了产品:17 消费者:在4位置取出了产品:17 生产者:在5位置放入了产品:18 消费者:在4位置取出了产品:18 生产者:在5位置放入了产品:19 消费者:在4位置取出了产品:19 消费者:在3位置取出了产品:6 消费者:在2位置取出了产品:4 消费者:在1位置取出了产品:3 消费者:在0位置取出了产品:2
Process finished with exit code 0
练习:用两种方式实现两个线程,一个线程负责打印1-2600,另一个线程打印A-Z,反复打印100遍
输出数字线程A类:
package com.XC_Theard.PrintA_Z;
public class Thr_A extends Thread{
@Override
public void run() {
for (int i = 1; i < 2600; i++) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出字母线程B类:
package com.XC_Theard.PrintA_Z;
public class Thr_B implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
for (char j = 'A'; j <= 'Z'; j++) {
System.out.println(j);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
测试类:
package com.XC_Theard.PrintA_Z;
public class Main {
public static void main(String[] args) {
Thread a = new Thr_A();
Runnable b1 = new Thr_B();
Thread b = new Thread(b1);
a.start();
b.start();
}
}
运行结果:
1 A 2 B 3 C D 4
……省略。。。
Process finished with exit code -1
|