1.多线程之Thread
一/创建
一/调用类之格式
1/
以空参构造创建,如果命名则通过setName方法命名.
格式:Thread线程继承类 自定义子线程对象 = new Thread线程继承类();
列如:ThreadClass threadClassObj = new ThreadClass();//
2/
以有参构造创建,直接通过括号内的String字符串命名.
格式:Thread线程继承类 自定义子线程对象 = new Thread线程继承类(String字符串);
列如:ThreadClass threadClassObjThree = new ThreadClass(“世界大同”);
二/继承类之格式:
格式:
public class Thread线程继承类 extends Thread {
@Override
public void run(){
//方法体
}
}
public class ThreadClass extends Thread {
@Override
public void run(){
//方法体
}
}
二/线程名称相关
一.通过get/set方法设置.
1.在Main方法内
通过set/get设置线程名
格式:自定义子线程对象.setName();
列如:threadClassObj.setName(“中华有为 - 华为”);
在Thread线程继承类内
2.通过继承方法来获取线程名
格式:getName();
列如:System.out.println(“线程名为[”+getName()+"]的数值为"+i);
二.通过有参构造方法设置.
1.在Main方法内
以有参构造创建线程,并给线程命名
格式:Thread线程继承类 自定义子线程对象 = new Thread线程继承类("自定义子线程名");
列如:ThreadClass threadClassObjThree = new ThreadClass(“世界大同”);
获取线程名
格式:自定义子线程对象.getName()
列如:System.out.println(threadClassObjThree.getName());
2.在Thread线程继承类内
通过有参构造并配合super给其父类传递名字.
格式:
public Thread线程继承类(String name){
super();
}
列如:
public ThreadClass(String name){
super();
}
三/方法应用
1/
启动线程
格式:对象名.strat();
列如:threadClassObj.start();
2/
等待线程死亡
格式:对象名.Join();
列如: try {
threadJoinObj.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
先让某个子线程的代码(即Run方法)全部执行完毕后,再去运行其它代码或运行其它子程序. 调用该方法需要处理异常.
3/
守护线程
格式:对象名.setDaemon(true / false);
列如:threadDaemonObj.setDaemon(true);
在子线程运行期间,如果主线程(即调用了子线程的方法,如’Main’)的代码全都执行完了,就停止当前所有在运行的子线程.
4/
暂停线程
格式:Thread线程继承类之Run方法{sleep(毫秒数);}
列如:public class ThreadSleep extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + “:” + i);
}
}
}
让当前线程在其Run方法内暂停一段时间(以毫秒为单位).再去执行接下来的代码. 一般用在Thread线程继承类的Run方法里头;用在主线程的话则需要异常处理.
5/
获取线程优先级
格式:线程对象名.getPriority();
列如:threadClassObj.getPriority();
6/
设置线程优先级
格式:线程对象名.setPriority(数值);
列如:threadClassObj.setPriority(5);
优先级数值表示范围为1-10 1最优先,10为最次.
2.多线程之Runnable(优先用它)
Runnable 这一种线程方式 推荐多用该方式,少用第一个方式
创建:
步骤1/
格式:Runnable线程实现类 自定义Runnable实现对象名 = new Runnable线程实现类();
列如:RunnableImplements runnableImplementsObj = new RunnableImplements();
步骤2/
格式:Thread 自定义Thread对象名 = new Thread(自定义Runnable实现对象名);
列如:Thread threadObj = new Thread(runnableImplementsObj);
注意:即使两个Thread对象指向同一个Runnable线程实现类对象,那也是两个独立的子线程.
如:
RunnableImplements runnableImplementsObj = new RunnableImplements();
Thread threadObj = new Thread(runnableImplementsObj);
Thread threadObjTwo = new Thread(runnableImplementsObj);
其中的’threadObj’和’threadObjTwo’尽管都指向了同一个Runable线程实现类对象’runnableImplementsObj’,但实际是两个互不冲突的子线程,他们之间并不会冲突,他们是个各自独立的子线程.
3.数据同步-解决多线程数据出错.
概念
数据同步,在多线程处理’共享数据(即:处理同一个run方法)‘时,如果不让其进行’数据同步’之处理,那么就会造成结果达不到预期的情况.
比如共有100张票,让线程去买票,买一次就减少一张.但如果不做’数据同步’的话,那么就会出现三个线程同买第XX张**(如三个都买第65张的票)**的错误情况.
深层次的问题之逻辑原因:是3个线程在处理共享数据时,是在相同时间点有一个以上的线程在共同处理某个数据造成的.
深层次逻辑的解决方案:既然是在相同时间点处理某个共同数据造成的,那么就让其排队.
比如说当’线程A’处理后,再让’线程B’处理,‘线程B’处理后,再让’线程C’,接着再’线程A’,如此循环就不会有数据出错的情况了.是为"排队"
接下来的3个解决方案,无不是运用了这个逻辑.
解决方案1/ 同步代码块
步骤1/ 声明一个Obj类型的成员变量,并为其赋值一个Object对象
格式:权限修饰符 Object 自定义成员变量名 = new Object();
列如:private Object objVariable = new Object();
步骤2/ 在需要进行同步处理的代码块上,加上’synchronized’同步数据声明语句,让其线程们排队执行该块代码
格式:synchronized(自定义成员变量名){需要进行数据同步的代码块};
列如:synchronized (objVariable) { //方案1 同步代码块
if (ticket > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “当前正在售出第” + this.ticket + “张票”);
ticket–;
}
}
解决方案2/ 同步静态方法
步骤1/ 声明一个同步数据之静态方法
格式:
权限修饰符 static synchronized 返回值类型 方法名(参数可选){
//需要被同步处理的方法体
}
列如:public static synchronized void staticMethod(){}
步骤2/ 调用
格式:方法名(参数可选);
列如:staticMethod();
解决方案3/ 同步动态方法
步骤1/ 声明一个同步数据之动态方法
格式:
权限修饰符 synchronized 返回值类型 方法名(参数可选){
//需要被同步处理的方法体
}
列如:public synchronized void dyMethod(){}
步骤2/ 调用
格式:方法名(参数可选);
列如:dyMethod();
4.Thread currentThread(重要)
以调用了该代码’Thread currentThread()'的线程为指针,可以通过该代码对当前[调用了该代码]的线程进行操作,即:如果是在main方法内调用了它,则对main线程进行操作.即:谁调用我,我就指向谁
格式:Thread.currentThread() / Thread.currentThread().子方法();
列如1:System.out.println(Thread.currentThread());//返回当前线程的名字
列如2:Thread.currentThread().setName(“中国”); //为执行这行代码的线程设置线程名字.
还有其它不少方法,这里不一一列出.
5.Object最高父类的多线程方法(重要):
在JAVA体系中,当无直接父类时,自动继承最高父类Object.
而Object则有 wait方法–等待线程 notify方法–唤醒线程 notifyAll方法–唤醒所有线程
方法
1/
线程睡眠.
格式:wait();
当程序运行到了其’wait()'调用处,那么就会让调用了其所处位置之方法的超线程暂停运行,称之为’睡眠.此时只有当其它线程的代码用到了’notifyAll’方法,才会让其恢复运行状态,
注意:'wait’等待方用于其所处方法为’synchronized’锁的情况下使用,如果其所处方法并非’synchronized’锁,那将会报错.
因为’synchronized’就是让子线程去’排队’,当线程A全部做完后才让线程B做.而’wait’等待方法则是让当前的子线程(如线程A)处于停止等待状态,让其它线程(如线程B)做完一次后,再轮到自己做.
套用在"抽奖案例",就是子线程A调用一次方法就用wait休眠,然后让子线程B调用一次.子线程B调用一次后就休眠从而让子线程A调.
2/ 唤醒线程
格式:notfy();
(?)**唤醒自己,也就是唤醒当前运行了此行代码的线程,**逻辑同wait的睡眠一样,哪个线程执行了它就对哪个线程操作
3/ 唤醒所有线程
格式:notfyAll
唤醒除自己以外的所有其它处于睡眠状态的线程
注意:套用在’抽奖案例’这样的相互地’你一次,我一次’排队处理数据和相互唤醒案例里头,唤醒必须要在’wait’等待的前面,不然线程A和B就只能运行一次.之后就会各自处于’等待’状态.
而’notifyALL’唤醒在前面,'wait’等待在后面,那么根据’多线程’的特性,也不过是启动了其它的线程而已,
并不会意味着本线程就会终止运行,既然不会终止运行,那么在’notifyAll’唤醒了其它线程后,本线程就可以运行’wait’来处于等待状态,接着再等待其它线程唤醒,如此循环往复下去.
多线程之Thread演示
public class MianTest {
public static void main(String[] args) {
//创建Thread线程继承类
ThreadClass threadClassObj = new ThreadClass();//线程1
ThreadClass threadClassObjTwo = new ThreadClass();//线程2
//创建Thread线程继承类,并通过有参构造直接为线程命名
ThreadClass threadClassObjThree = new ThreadClass("世界大同");
//获取线程名
System.out.println(threadClassObjThree.getName());
//设置线程名
threadClassObj.setName("中华有为 - 华为");
threadClassObjTwo.setName("华夏归真 - 华真");
//获取线程优先级 对象名.getPriority();
threadClassObj.getPriority();
threadClassObjTwo.getPriority();
threadClassObjThree.getPriority();
//设置线程优先级 对象名.setPriority();
threadClassObj.setPriority(5);
//以Strat方法开启线程,即:通过Strat方法以多线程方式来,接调地用run方法,而不是直接调用run方法.
threadClassObj.start();
System.out.println("你好!打印输出!");
//如果main方法中有[调用线程的代码(start)],跟main方法的代码[列如:System.out.println("你好!打印输出!")]是紧紧挨在一块的,
// 且main方法的代码就在[调用线程的代码(strat)]下面,那么可能会先执行main方法的代码.
threadClassObjTwo.start();
threadClassObjThree.start();
//Thread currentThread() 返回当前调用了该方法'Thread.currentThread()'的线程名字.如果是Main方法调用它,则返回Main;如果是其它方法调用它,则返回其它方法的名字.
System.out.println(Thread.currentThread());
Thread.currentThread().setName("中国"); //为执行这行代码的线程设置线程名字.
System.out.println("=======================");
joinRun();
System.out.println("=======================");
daemonRun();
}
//ThreadJoin演示 对象名.Join(); 等待线程死亡
//先让某个子线程的代码(即Run方法)全部执行完毕后,再去运行其它代码或运行其它子程序.
public static void joinRun(){
ThreadJoin threadJoinObj = new ThreadJoin();
ThreadJoin threadJoinObjTwo = new ThreadJoin();
ThreadJoin threadJoinObjThree = new ThreadJoin();
threadJoinObj.setName("刘备");
threadJoinObjTwo.setName("张飞");
threadJoinObjThree.setName("关羽");
//启动线程
threadJoinObj.start();
//让子线程'threadJoinObj'调用Join方法,以让其该子线程的代码全部执行完毕后再去做其它事情.
try {
threadJoinObj.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
threadJoinObjTwo.start();
threadJoinObjThree.start();
}
//线程守护演示 对象名.setDaemon(); 守护线程
// 在子线程运行期间,如果主线程(即调用了子线程的方法,如'Main')的代码全都执行完了,就停止当前所有在运行的子线程.
public static void daemonRun(){
ThreadDaemon threadDaemonObj = new ThreadDaemon();
ThreadDaemon threadDaemonObjTwo = new ThreadDaemon();
threadDaemonObj.setName("线程守护 - 关于");
threadDaemonObjTwo.setName("线程守护 - 张飞");
threadDaemonObj.setDaemon(true);
threadDaemonObjTwo.setDaemon(true);
threadDaemonObj.start();
threadDaemonObjTwo.start();
for (int i = 0; i < 100; i++) {
System.out.println("主线程demonRun的数值为:"+i);
}
}
//暂停线程 Run方法内{sleep(毫秒数)}
//让当前线程在其Run方法内暂停一段时间(以毫秒为单位).再去执行接下来的代码.
public static void sleepRun() throws InterruptedException {
ThreadDaemon threadDaemonObj = new ThreadDaemon();
ThreadDaemon threadDaemonObjTwo = new ThreadDaemon();
threadDaemonObj.setName("暂停线程 - 关于");
threadDaemonObjTwo.setName("暂停线程 - 张飞");
threadDaemonObj.start();
threadDaemonObjTwo.start();
}
}
多线程之Runnable演示
main主方法
public class MainClass {
public static void main(String[] args) {
RunnableImplements runnableImplementsObj = new RunnableImplements();
Thread threadObj = new Thread(runnableImplementsObj);
Thread threadObjTwo = new Thread(runnableImplementsObj);
}
}
线程实现类
public class RunnableImplements implements Runnable {
@Override
public void run() {
System.out.println("🐏");
}
}
数据同步演示
买票案例
Main主方法
public class BuyTicket {
public static void main(String[] args) {
BuyTicketImplements buyTicketImplements = new BuyTicketImplements();
Thread threadObj = new Thread(buyTicketImplements,"窗口1");
Thread threadObjTwo = new Thread(buyTicketImplements,"窗口2");
Thread threadObjThree = new Thread(buyTicketImplements,"窗口3");
//启动窗口
threadObj.start();
threadObjTwo.start();
threadObjThree.start();
}
线程实现类
public class BuyTicketImplements implements Runnable{
private static int ticket = 100;
private Object objVariable = new Object();
@Override
public void run() {
while (true){
synchronized (objVariable) { //方案1 同步代码块
if (ticket > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "当前正在售出第" + this.ticket + "张票");
ticket--;
}
}
//方案2 同步动态方法
dyMethod();
//方案3 同步静态方法
staticMethod();
}
}
//方案2 同步静态方法
public static synchronized void staticMethod(){
if (ticket > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "当前正在售出第" + ticket + "张票");
ticket--;
}
}
//方案3 同步动态方法
public synchronized void dyMethod(){
if (this.ticket > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "当前正在售出第" + this.ticket + "张票");
this.ticket--;
}
}
}
唤醒/睡眠演示案例
奶箱案例
分别有4个类
1/Test.java 测试类
2/Box.java 奶箱类
3/Customer.java 消费者类
4/Producer.java 生产者类
1/Test.java 测试类
public class Test {
public static void main(String[] args) {
Box boxObj = new Box();
//创建生产者对象 把奶箱当作参数传入
Producer producerObj = new Producer(boxObj);
//创建消费者对象 把奶箱当作参数传入
Customer customerObj = new Customer(boxObj);
//创建消费者和生产者的线程对象
Thread producerThreadObj = new Thread(producerObj);
Thread customerThreadObj = new Thread(customerObj);
producerThreadObj.start();
customerThreadObj.start();
}
}
2/Box.java 奶箱类
public class Box {
private int boxNum;
private boolean boxInfo = false;
public synchronized void boxPut(int boxNum) {
if (boxInfo) { //如果boxInfo 为true
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.boxNum = boxNum;
System.out.println("生产第" + this.boxNum + "瓶牛奶");
boxInfo = true;
notifyAll();
}
public synchronized void boxGet() {
if (boxInfo!=true) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("用户拿到第" + this.boxNum + "瓶牛奶");
this.boxInfo = false;
notifyAll();
}
}
3/Customer.java 消费者类
public class Customer implements Runnable {
private Box boxParamObj;
public Customer(Box boxObj) {
this.boxParamObj = boxObj;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
boxParamObj.boxGet();
}
}
}
4/Producer.java 生产者类
public class Producer implements Runnable {
private Box boxObjOfParam;
public Producer(Box boxObj) {
this.boxObjOfParam = boxObj;
}
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
boxObjOfParam.boxPut(i);
}
}
}
重要!自创抽奖案例!
【编号2004】有一个抽奖池,里面存放了一些固定金额的奖金
int[] prizePool = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
此时,有两个人A和B轮流在奖箱中随机抽奖(即A抽一次–>B抽一次–>A抽一次–>B抽一次…)
,直到所有的奖金都被抽出,每次抽奖耗时1秒。请编写程序,使用两个线程模拟两人的抽奖过程,输出结果如下(金额的顺序随机):
A抽出奖金600
B抽出奖金1000
A抽出奖金900
B抽出奖金300
A抽出奖金500
B抽出奖金200
A抽出奖金700
B抽出奖金100
A抽出奖金800
B抽出奖金400
主方法类Prize.java
public class Prize {
public static void main(String[] args) {
Box boxObj = new Box();
PersonA psA = new PersonA(boxObj);
PersonB psB = new PersonB(boxObj);
Thread tOne = new Thread(psA);
Thread tTwo = new Thread(psB);
tOne.setName("A");
tTwo.setName("B");
tOne.start();
tTwo.start();
}
}
线程A类PersonA.java
package code.study.school0104prize;
public class PersonA implements Runnable {
private Box boxvariable;
private int count;
public PersonA(Box boxObj) {
this.boxvariable = boxObj;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
boxvariable.method();
}
}
}
线程B类PersonB.java
package code.study.school0104prize;
public class PersonB implements Runnable{
private Box boxvariable;
private int count;
public PersonB(Box boxObj) {
this.boxvariable = boxObj;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
boxvariable.method();
}
}
}
抽奖箱类Box.java
package code.study.school0104prize;
import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Currency;
import java.util.Random;
/*
纯手工打造!
*/
public class Box {
private int[] prizePool = {100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
private int count = 0;
private ArrayList<Integer> arr;
private boolean okAndNo = true;
public Box() {
arr = new ArrayList<>();
for (int i = 0; i < prizePool.length; i++) {
arr.add(prizePool[i]);
}
}
public synchronized void method() {
//注意:'wait'等待方用于其所处方法为'synchronized'锁的情况下使用,如果其所处方法并非'synchronized'锁,那将会报错.
//因为'synchronized'就是让子线程去'排队',当线程A全部做完后才让线程B做.而'wait'等待方法则是让当前的子线程(如线程A)处于停止等待状态,让其它线程(如线程B)做完一次后,再去做.
//套用在这里,就是子线程A调用一次方法就用wait休眠,然后让子线程B调用一次.子线程B调用一次后就休眠从而让子线程A调.
Random r = new Random();
int num;
Integer deleteNum = null;
if (arr.size() > 0) {
num = r.nextInt(arr.size());
deleteNum = arr.remove(num);
// System.out.println("集合长度"+arr.size());
}
System.out.println(Thread.currentThread().getName() + "抽中了" + deleteNum + "元");
try {
notifyAll();//唤醒 唤醒必须要在'wait'等待的前面,不然线程A和B就只能运行一次.之后就会各自处于'等待'状态.
//而'notifyALL'唤醒在前面,'wait'等待在后面,那么根据'多线程'的特性,也不过是启动了其它的线程而已,
// 并不会意味着本线程就会终止运行,既然不会终止运行,那么在'notifyAll'唤醒了其它线程后,本线程就可以运行'wait'来处于等待状态,接着再等待其它线程唤醒,如此循环往复下去.
if (arr.size()!=0){//如果集合长度为0,即:集合没有元素,则视为抽奖完毕,什么都不做线程就可以结束了.如果在抽奖完毕的情况下仍然去用'wait'等待,那线程将无法结束.
//因此此处
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println(Thread.currentThread().getName() + " " + count);
}
}