Java多執行緒實作簡易微信發紅包
一、
首先我們先大致了解一下什么是多執行緒,(書上的解釋)
程式是一段靜態的代碼,它是應用軟體的藍本,行程是程式的一次動態執行程序,對應了從代碼加載執行,執行到執行完畢的一個完整的程序,
執行緒不是行程,執行緒是比行程更小的執行單位,一個行程在其執行程序中,可以產生多個執行緒形成多條執行線索,每條線索即每個執行緒也有它自身的產生,存在,消亡的程序,和行程共享作業系統的資源類似,執行緒間也可以共享行程中的某些記憶體單元,并利用這些共享單元來實作資料交換,實時通信與必要的同步操作,但與行程不同的是執行緒的中斷和恢復更加節省開支,執行緒是運行在行程中的“小行程”,
多執行緒是指一個應用程式中同時存在幾個執行體,按幾條不同的執行線索共同作業的情況,雖然看似是幾個事件同時發生,但其實計算機在任何給定時刻只能執行那些執行緒中的一個,為了建立這些執行緒在同步進行的感覺,Java虛擬機快速的把控制從一個執行緒切換到另一個執行緒,這些執行緒將被輪流執行,使得每個執行緒都有機會使用CPU資源,
二、
利用單執行緒實作的簡易微信發紅包
共寫有三種方法,其中第一種,第二種未設定范圍,紅包數和人數為一一對應,第三種增添了取值范圍以及計數器,人多紅包少有未搶到現象發生,
(1) 方法一
import java.util.Scanner;
import com.sun.deploy.security.SelectableSecurityManager;
import java.util.Random;
public class 簡易微信發紅包 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n;
double money;
System.out.println("請輸入您想要發的紅包數量");
n=scanner.nextInt();
System.out.println("請輸入您發送的紅包金額");
money=scanner.nextDouble();
T2 t2=new T2(n,money);
t2.Rob();
}
}
class T2 {
public double remain;//有紅包被領取后的余額
int n;//紅包數量
T2(int n,double money) {
this.remain=money;
this.n=n;
}
int a=1;
public void Rob() {
while (n > 0) {
double x2;
if (n != 1) {//因為最后一個人領取金額為前面人領取紅包后剩下的,所以無需再進行隨機
x2 = process();//取隨機金額
while (judge(x2) != 1) {//判斷取到的隨機金額是否非法,即無法保證后來每個紅包領取者領到最低金額0.01
x2 = process();//若非法則重新取隨機金額
}
remain = remain - x2;//當領取成功后余額減去領走的金額
n--;//確保每次判斷人數為所剩紅包數減1
System.out.println("紅包獲得者" + a + "獲得" + x2 + "元");//此處默認領取者順序為升序
a++;//控制輸出順序
}
else {
x2 = remain;//因為最后一個人領取金額為前面人領取紅包后剩下的,所以無需再進行隨機
String str = String.valueOf(x2);
String str1 = String.format("%.2f", x2);
x2 = Double.parseDouble(str1);
System.out.println("紅包獲得者" + a + "獲得" + x2 + "元");
n--;//確保每次判斷人數為所剩紅包數減1
}
}
}
public int judge(double x){//判斷函式
if(remain-x>(n-1)*0.01){//確保后來紅包領取者最少能領到最低金額0.01
return 1;
}
else return 0;
}
public double process() {//實作紅包金額隨機的函式
double x2;
double x1;
String str1;
Random random = new Random();//亂數為取0到1之間的任意double值
x1 = remain*random.nextDouble();
str1= String.format("%.2f",x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x2=Double.parseDouble(str1);//再將字串型資料轉換成double型
while(x2==0){//如果所取金額非法則回爐重造
x1 = remain*random.nextDouble();
str1= String.format("%.2f",x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x2=Double.parseDouble(str1);//再將字串型資料轉換成double型
}
return x2;
}
}
程式運行結果如下

(2) 方法二
import java.util.Random;
import java.util.Scanner;
public class 簡易微信發紅包2 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
double money=0;//紅包總金額
int n;//紅包個數
System.out.println("請輸入您想要發的紅包數量");
n=scanner.nextInt();
System.out.println("請輸入您發送的紅包金額");
money=scanner.nextDouble();
if(money/n==0.01){//當所發金額剛好為每人0.01元時
T6 t6=new T6(money,n);
t6.Rob();
}else{
T5 t5=new T5(money,n);
t5.Rob();
}
}
}
class T5{
double remain;
int n;
T5(double money,int n){
this.remain=money;
this.n=n;
}
int a=1;
public void Rob(){
double max;//最大可領紅包金額
double x1;//隨機金額
double x2;//所得金額
while(n>0) {
if (n != 1) {//前n-1個紅包領取者領的紅包為隨機金額紅包
max = remain - (n - 1) * 0.01;//最大可領紅包金額為剩下的人都獲得最小金額0.01
Random random = new Random();
x1 = (double) random.nextInt((int) ((max - 0.01) * 100));
//用nextInt而不用nextDouble的原因是nextDouble無法設定seed
//上式中max-0.01,下面的x2+0.01即解決了亂數取0導致紅包獲得者沒搶到錢的問題
x1 /= 100.0;
x2 = x1 + 0.01;
remain = remain - x2;
n--;
System.out.println("紅包獲得者" + a + "獲取金額為:" + String.format("%.2f", x2) + "元");
a++;
} else {//最后一人領的紅包為前n-1個人領完后剩下的紅包
System.out.println("紅包獲得者" + a + "獲取金額為:" + String.format("%.2f", remain) + "元");
n--;
}
}
}
}
class T6 {
double remain;
int n;
T6(double money,int n){
this.remain=money;
this.n=n;
}
public void Rob(){
for(int i=1;i<=n;i++){
System.out.println("紅包獲得者"+i+"獲得了0.01元");
}
}
}
程式運行結果如下:


(3) 方法三
import java.util.Random;
import java.util.Scanner;
public class 簡易微信發紅包3 {
public static void main(String[] args) {
int p,n;
double money;
System.out.println("請輸入您發送的紅包金額");
Scanner scanner=new Scanner(System.in);
money=scanner.nextDouble();
System.out.println("請輸入您發送的紅包數量");
n=scanner.nextInt();
System.out.println("請輸入參與搶紅包的人數");
p=scanner.nextInt();
T7 t7=new T7(money,n,p);
t7.Rob();
}
}
class T7 {
double money;
int n,p;
int count =0;//計數器
double remain;
T7(double money,int n,int p){
this.money=money;//總金額
this.n=n;//紅包數
this.p=p;//搶紅包人數
this.remain=money;//所剩金額
}
public void Rob() {
for(int i=1;i<=p;i++) {
double x1, x2, d;
String s1, s2;
Random random = new Random();
d = money / (n - 1);//設定范圍讓每次所得金額不超過總數的1/(n-1),這樣也就避免了一次取得過大導致后面搶的紅包不能保證每個最少0.01
x1 = d * random.nextDouble();
s1 = String.format("%.2f", x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x1 = Double.parseDouble(s1);//再將字串型資料轉換成double型
while (x1 == 0 || x1 == money / (n - 1)) {
x1 = d * random.nextDouble();
s1 = String.format("%.2f", x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x1 = Double.parseDouble(s1);//再將字串型資料轉換成double型
}
s2 = String.format("%.2f", remain);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
remain = Double.parseDouble(s2);//再將字串型資料轉換成double型
if (count < n - 1) {//前n-1個紅包金額為隨機金額
System.out.println( "紅包搶奪者"+i+ "搶到了" + s1 + "元");
remain -= x1;
count++;
} else if (count == n - 1) {//第n個為前n-1個紅包搶完所剩金額
System.out.println( "紅包搶奪者"+i+ "搶到了" + s2 + "元");
count++;
} else if (count > n - 1) {//紅包被搶完后再來的
System.out.println( "紅包搶奪者"+i+ "哎呀,手慢了!沒搶到!");
count++;
}
}
}
}
程式運行結果如下:

三、
利用多執行緒實作的簡易微信發紅包
那么如何創建多執行緒呢?
1.通過繼承thread類創建多執行緒
JDK中提供了一個執行緒類Thread,通過繼承Thread類,并重寫Thread類中的run()方法便可實作多執行緒,
在Thread類中,提供了一個start()方法用于啟動新執行緒,執行緒啟動后,系統會自動呼叫run()方法,如果子類重寫了該方法便會執行子類中的方法,
run()方法中就是寫能夠被執行緒執行的程式,如果直接呼叫則相當于普通方法,必須使用start()方法,才能啟動執行緒,然后再由JVM去呼叫該執行緒的run()方法,
創建并啟動多執行緒的步驟
①定義Thread類的子類,并重寫該類的run方法,其方法體代表執行緒需要完成的任務,因此常把run方法稱為執行緒執行體,
②創建Thread子類的實體,即創建執行緒物件,
③用執行緒物件的start方法來啟動該執行緒,
2.通過實作Runnable介面創建多執行緒
通過繼承Thread類實作了多執行緒,但是這種方式有一定的局限性,因為Java中只支持單繼承,一個類一旦繼承了某個父類就無法再繼承Thread類,
Thread類提供了另外一個構造方法Thread(Runnable target),其中Runnable是一個介面,它只有一個run()方法,
當通過Thread(Runnable target))構造方法創建執行緒物件時,只需為該方法傳遞一個實作了Runnable介面的實體物件,這樣創建的執行緒將呼叫實作了Runnable介面中的run()方法作為運行代碼,而不需要呼叫Thread類中的run()方法,
創建并啟動多執行緒的步驟
①定義Runnable介面的實作類,并重寫該介面的run方法,該run方法的方法體同樣是該執行緒的執行緒執行體,
②創建Runnable實作類的實體,并以此為實體作為Thread的引數來創建Thread物件,該Thread物件才是真正的執行緒物件,
當多個執行緒使用同一個共享資源時,可以將處理共享資源的代碼放置在一個代碼塊中,使用synchronized關鍵字來修飾,被稱作同步代碼塊:
sychronized(lock){
操作共享資源代碼塊
}
其中:lock是一個鎖物件,它是同步代碼塊的關鍵,當執行緒執行同步代碼塊時,首先會檢查鎖物件的標志位,默認情況下,標志位為1,此時執行緒會執行同步代碼塊,同時將鎖物件的標志位置為0,當一個新的執行緒執行到這段同步代碼塊時,由于鎖物件的標志位為0,新執行緒會發生阻塞,等待當前執行緒執行完同步代碼塊后,鎖物件的標志位被置為1,新執行緒才能進入同步代碼塊執行其中的代碼,回圈往復,直到共享資源被處理完為止,
同步代碼塊可以有效解決執行緒的安全問題,當把共享資源的操作放在synchronized定義的區域內時,便為這些操作加了同步鎖,
在方法前面同樣可以使用synchronized關鍵字來修飾,被修飾的方法為同步方法,它能實作和同步代碼塊同樣的功能,具體語法格式如下:
synchronized 回傳值型別 方法名 {}
被synchronized修飾的方法在某一時刻只允許一個執行緒訪問,訪問該方法的其它執行緒都會發生阻塞,直到當前執行緒訪問完畢后,其它執行緒才有機會執行方法,
另外
public final String getName():獲取執行緒的名稱,
public static Thread currentThread():回傳當前正在執行的執行緒物件,這樣就可以獲取任意方法所在的執行緒名稱,
Thread.currentThread().getName()
現將上面的單執行緒改成多執行緒實作
本篇文章多執行緒的創建以及實作用Runnable介面實作
(1)
import java.util.Scanner;
import com.sun.deploy.security.SelectableSecurityManager;
import java.util.Random;
public class 微信發紅包多執行緒 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
int n;
double money;
System.out.println("請輸入您想要發的紅包數量");
n=scanner.nextInt();
System.out.println("請輸入您發送的紅包金額");
money=scanner.nextDouble();
T3 t3=new T3(n,money);//創建runnable實作類的實體
for (int j = 1; j <= n; j++) {
new Thread(t3, "紅包獲得者" + j).start();//以上面創建的實體作為Thread的引數來創建Thread物件,并為Thread物件指定一個名字,用執行緒物件的start方法來啟動該執行緒,
}
}
}
class T3 implements Runnable {//實作runnable介面
public double remain;//有紅包被領取后的余額
int n;//紅包數量
public synchronized void run() {//同步方法,在某一時刻只允許一個執行緒訪問,防止資料錯亂
Rob();
}
T3(int n, double money) {
this.remain = money;
this.n = n;
}
int a = n;
public void Rob() {
double x2;
if (n != 1) {//因為最后一個人領取金額為前面人領取紅包后剩下的,所以無需再進行隨機
x2 = process();//取隨機金額
while (judge(x2) != 1) {//判斷取到的隨機金額是否非法,即是否能保證后來每個紅包領取者領到最低金額0.01
x2 = process();//若非法則重新取隨機金額
}
remain = remain - x2;//當領取成功后余額減去領走的金額
n--; //確保每次判斷人數為紅包數減一
} else {
x2 = remain;//因為最后一個人領取金額為前面人領取紅包后剩下的,所以無需再進行隨機
String str = String.valueOf(x2);
String str1 = String.format("%.2f", x2);
x2 = Double.parseDouble(str1);
}
Thread th = Thread.currentThread();//回傳當前正在執行的執行緒物件
String th_name = th.getName();//獲取執行緒的名稱
System.out.println(th_name + "搶到" + x2 + "元");
}
public int judge(double x) {//判斷函式
if (remain - x > (n - 1) * 0.01) {//確保后來紅包領取者能領到最低金額0.01
return 1;
} else return 0;
}
public double process() {//實作紅包金額隨機的函式
double x2;
double x1;
String str1;
Random random = new Random();//亂數為取0到1之間的任意double值
x1 = remain * random.nextDouble();
str1 = String.format("%.2f", x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x2 = Double.parseDouble(str1);//再將字串型資料轉換成double型
while (x2 == 0) {//如果所取金額非法則回爐重造
x1 = remain * random.nextDouble();
str1 = String.format("%.2f", x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x2 = Double.parseDouble(str1);//再將字串型資料轉換成double型
}
return x2;
}
}
程式運行結果如下:

(2)
import java.util.Random;
import java.util.Scanner;
public class 簡易微信發紅包多執行緒2 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
double money=0;//紅包總金額
int n;//紅包個數
System.out.println("請輸入您想要發的紅包數量");
n=scanner.nextInt();
System.out.println("請輸入您發送的紅包金額");
money=scanner.nextDouble();
if(money/n==0.01){//當所發金額剛好為每人0.01元時
T4 t4=new T4(money,n);
for(int i=1;i<=n;i++) {
new Thread(t4,"紅包獲得者"+i).start();
}
}else{
T1 t1=new T1(money,n);
for(int i=1;i<=n;i++) {
new Thread(t1,"紅包獲得者"+i).start();
}
}
}
}
class T1 implements Runnable{
double remain;
int n;
T1(double money,int n){
this.remain=money;
this.n=n;
}
@Override
public synchronized void run() {
Rob();
}
public void Rob(){
double max;//最大可領紅包金額
double x1;//隨機金額
double x2;//所得金額
if(n!=1) {//前n-1個紅包領取者領的紅包為隨機金額紅包
max=remain-(n-1)*0.01;//最大可領紅包金額為剩下的人都獲得最小金額0.01
Random random=new Random();
x1=(double)random.nextInt((int) ((max-0.01)*100));
//用nextInt而不用nextDouble的原因是nextDouble無法設定seed
//上式中max-0.01,下面的x2+0.01即解決了亂數取0導致紅包獲得者沒搶到錢的問題
x1/=100.0;
x2=x1+0.01;
remain=remain-x2;
n=n-1;
Thread th=Thread.currentThread();//獲取當前執行緒
String th_name=th.getName();//獲取執行緒名字
System.out.println(th_name+"獲取金額為:"+String.format("%.2f", x2)+"元");
}
else {//最后一人領的紅包為前n-1個人領完后剩下的紅包
Thread th=Thread.currentThread();//獲取當前執行緒
String th_name=th.getName();//獲取執行緒名字
System.out.println(th_name+"獲取金額為:"+String.format("%.2f", remain)+"元");
}
}
}
class T4 implements Runnable{
double remain;
int n;
T4(double money,int n){
this.remain=money;
this.n=n;
}
public synchronized void run() {
Rob();
}
public void Rob(){
Thread th=Thread.currentThread();//獲取當前執行緒
String th_name=th.getName();//獲取執行緒名字
System.out.println(th_name+"獲取金額為:"+String.format("%.2f", remain/n)+"元");
}
}
程式運行結果如下:


(3)
import java.util.Random;
import java.util.Scanner;
public class 簡易微信發紅包多執行緒3 {
public static void main(String[] args) {
int p,n;
double money;
System.out.println("請輸入您發送的紅包金額");
Scanner scanner=new Scanner(System.in);
money=scanner.nextDouble();
System.out.println("請輸入您發送的紅包數量");
n=scanner.nextInt();
System.out.println("請輸入參與搶紅包的人數");
p=scanner.nextInt();
HH hh=new HH(money,n);
for (int i=1;i<=p;i++){
new Thread(hh,"第"+i+"個人").start();
}
}
}
class HH implements Runnable{
double money;
int n;
int count =0;//計數器
double remain;
HH(double money,int n){
this.money=money;//總金額
this.n=n;//紅包數
this.remain=money;//所剩金額
}
@Override
public synchronized void run() {
Rob();
}
public void Rob(){
double x1,x2,d;
String s1,s2;
Thread th=Thread.currentThread();//獲取當前執行緒
String th_name=th.getName();//獲取執行緒名字
Random random=new Random();
d=money/(n-1);//設定范圍讓每次所得金額不超過總數的1/(n-1),這樣也就避免了一次取得過大導致后面搶的紅包不能保證每個最少0.01
x1=d*random.nextDouble();
s1=String.format("%.2f",x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x1 = Double.parseDouble(s1);//再將字串型資料轉換成double型
while(x1==0||x1==money/(n-1)){
x1=d*random.nextDouble();
s1=String.format("%.2f",x1);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
x1 = Double.parseDouble(s1);//再將字串型資料轉換成double型
}
s2= String.format("%.2f",remain);//轉化成字串型用字串型的format進行格式化處理,紅包金額最多取到小數點后兩位
remain = Double.parseDouble(s2);//再將字串型資料轉換成double型
if (count<n-1){//前n-1個紅包金額為隨機金額
System.out.println(th_name+"搶到了"+s1+"元");
remain-=x1;
count++;
}else if (count==n-1){//第n個為前n-1個紅包搶完所剩金額
System.out.println(th_name+"搶到了"+s2+"元");
count++;
}else if (count>n-1){//紅包被搶完后再來的
System.out.println(th_name+"哎呀,手慢了!沒搶到!");
count++;
}
}
}
程式運行結果如下:

新手上路,因能力有限,若有不足之處還望大家海涵!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/254536.html
標籤:其他
