文章目录
1. 进制转换
写出一个程序,接受一个十六进制的数,输出该数值的十进制表示。
[推荐、简单]思路:java内置 decode方法,将 String 解码为 Integer
直接调用Java的decode方法.
该方法的作用是将 String 解码为 Integer。接受十进制、十六进制和八进制数字。
根据要解码的 String(mn)的形式转成不同进制的数字。 mn由三部分组成:符号、基数说明符和字符序列。 -0X123中-是符号位,0X是基数说明符(0表示八进制,0x,0X,#表示十六进制,什么都不写则表示十进制),123是数字字符序列。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
while (sc.hasNext()){
String str=sc.next();
System.out.println(Integer.decode(str));
}
}
}
【不推荐】思路2,自己计算
16进制 --> 10进制 思路: 比如:0x209
- 去掉 “ox”就剩下 209 (三位数:个-9、十-0、百-2)
- 10进制数 = 2 × 16^2 + 0 × 16^1 + 9 × 16^0 = 521
2. 质数因子
题目描述
功能:输入一个正整数,按照从小到大的顺序输出它的所有质因子(重复的也要列举)(如180的质因子为2 2 3 3 5 )
最后一个数后面也要有空格
输入描述:
输入一个long型整数
输出描述:
按照从小到大的顺序输出它的所有质数的因子,以空格隔开。最后一个数后面也要有空格。
思路1: 设定i=2,i一直递增,当N除以i余数为0的时候,N设定为N/i
这道题,需要一些数学基础。
质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
质因子牵涉到两个概念:因子和质数;
因子,又叫“因数”或“约数”,如果整数a能被整数b整除,那就有一个整数q,使得 a=bq,则b和q都称为a的一个因子.15=3乘以5,所以3和5都是15的因子;8=2乘以4,那么2和4 就是8的因子。 质数,又叫“素数”,在大于1的自然数中,仅有1和本身为自己的因子的数叫素数。7 只有1、7这两个因子,所以是素数。8有1、2、4、8四个因子,除了1、8,还有2和4,所以就不是素数.
分解质因数的方法是先用一个合数的最小质因数去除这个合数,得出的数若是一个质数,就写成这个合数相乘形式;若是一个合数就继续按原来的方法,直至最后是一个质数 。
常规思路每一次遍历一遍2到N的全部的整数,找到一个质数因子a,然后将N设置为N/a,直到N等于1,但是这种方法其实就是暴力搜索,时间效率并不好。
其实有一种更好的方法,就是设定i=2,i一直递增,当N除以i余数为0的时候,N设定为N/i,否则i++,直到i>N,这样找到的所有N除以i余数为0的i就是N的所有的质数因子。
这样为什么可行呢?
我们假设从2开始,找到的第一个N除以i余数为0的i为a1,首先a1一定是质数,因为假如a1是合数的话在2和a1之间一定存在其他N可以整除的质数,但是i是从2开始找到的第一个可以整除的数,因此i只能是质数,也就是说i是N最小的质因子。
然后从a1继续往后找,找到第二个N可以整除的数a2,a2不可能是合数,因为假如a2是和数的话,2到a1,或者是a1到a2之间一定存在没有分解的质数,而这是不可能的,所以a2一定是质数,且是N第二大的质因子,进行将N设定为N/a2。
继续以上操作,当找到最后一个质因子的时候,N==i,这个时候算法结束,至此,N的所有质因子都找到了。
import java.util.Scanner;
public class Main{
public static void handler(int num) {
while(num != 1) {
for(int i = 2; i <= num; ++i) {
if( num % i == 0) {
System.out.print( i + " ");
num = num / i;
break;
}
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
handler(sc.nextInt());
}
}
}
3. 合并表记录
题目描述
数据表记录包含表索引和数值(int范围的整数),请对表索引相同的记录进行合并,即将相同索引的数值进行求和运算,输出按照key值升序进行输出。
输入描述:
先输入键值对的个数
然后输入成对的index和value值,以空格隔开
输出描述:
输出合并后的键值对(多行)
示例1
输入:
4
0 1
0 2
1 2
3 4
输出:
0 3
1 2
3 4
思路:使用有序的TreeMap
此题如果直接使用有序的TreeMap就不需要这样折腾:
1.map的key值唯一性,故就不在需要set集合来去重
2.使用map后利用key的唯一性,把序列号相同的数据直接加在一起,代码会很简洁
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
TreeMap<Integer,Integer> treeMap = new TreeMap<>();
while (num>0){
int key = sc.nextInt();
int value = sc.nextInt();
if (!treeMap.containsKey(key)){
treeMap.put(key,value);
}else{
treeMap.put(key,Math.addExact(treeMap.get(key),value));
}
num--;
}
for (Integer key : treeMap.keySet()) {
System.out.println(key+" "+treeMap.get(key));
}
}
}
4. 提取不重复的整数
1,题目描述:输入一个int型整数,按照从右往左的阅读顺序,返回一个不含重复数字的新的整数
2,输入描述:输入一个int型整数
3,输出描述:按照从右到左的阅读顺序,返回一个不含重复数字的新的整数
4,输入例子:8976673
5,输出例子:37689
思路1 创建一个长度为10的数组
使用java写一个程序:输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数。
参考URL: https://blog.csdn.net/weixin_45710839/article/details/105695392
int num = numStr.charAt(i) - 48;是什么意思?
把char型数字转换成的int型数字,因为它们的ASCII码值恰好相差48
因此把char型数字减去48得到int型数字,例如’4’被转换成4。
该思路比较好,推荐掌握。
import java.util.Scanner;
//输入一个int型整数,按照从右向左的阅读顺序,返回一个不含重复数字的新的整数。
public class Demo6 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
//因为要返回一个不含重复数字的新整数,这里我们要对已经有的数做出表示,且最多只有10个数字
//这里我们创建一个长度为10的数组
int[] arr = new int[10];
for (int i = s.length() - 1; i >=0; i--) {
if (arr[s.charAt(i) - 48] == 0) {
System.out.print(s.charAt(i)-48);
arr[s.charAt(i) - 48]++;
}
}
}
}
5. 字符个数统计
题目描述
编写一个函数,计算字符串中含有的不同字符的个数。字符在ACSII码范围内(0~127),换行表示结束符,不算在字符里。不在范围内的不作统计。多个相同的字符只计算一次
例如,对于字符串abaca而言,有a、b、c三种不同的字符,因此输出3。
输入描述:
输入一行没有空格的字符串。
输出描述:
输出范围在(0~127)字符的个数。
例1
输入
abc
输出
3
HashSet
涉及到去重的时候,又是单列表,首先想到HashSet,唯一的难点是判断字符的ASCII码在(0-127)这个范围内;其实只要把字符强转成整型,就是其对应的ASCII码了。这种情况下只要判断 该值是否在0-127内就可以了。
char型与int的相互转化
char与int的相互转化,联想ASCII码,字符‘0’对应的值为48,所以不能直接加减‘ ’
如何把 char ‘9’ 转为 int 9, 大家应该知道,不能直接转化,那样得到是‘9’的Ascii
-
char转int
char ch=‘9’;
int ch_int=ch-‘0’;//此时ch_int=9 -
int转char
int i=9;
char i_ch=i+‘0’;//此时i_ch=‘9’
总结:经过测试, 默认会自动转换成字符对应的ascii码,char 直接复制给int,也可以自动转换。如: int i = ch;
根据业务需要,看自己属于哪种情况。本例中要的就是字符的本身的acsii吗 在0-127之间,所以直接编码为 int i =c;
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc =new Scanner(System.in);
HashSet<Integer> set = new HashSet<>();
//HashSet<Character> set = new HashSet<>();
//while(sc.hasNext("")){
String s = sc.nextLine();
char[] charArray = s.toCharArray();
for(char c : charArray){
int i =c;
if(i>=0 && i<=127){
set.add(i);
}
}
System.out.println(set.size());
//}
}
}
6. 字符串排序
题目描述
给定n个字符串,请对n个字符串按照字典序排列。
输入描述:
输入第一行为一个正整数n(1≤n≤1000),下面n行为n个字符串(字符串长度≤100),字符串中只含有大小写字母。
输出描述:
数据输出n行,输出结果为按照字典序排列的字符串。
示例1
输入
9
cap
to
cat
card
two
too
up
boat
boot
输出
boat
boot
cap
card
cat
to
too
two
up
思路:利用java 的 Arrays.sort 排序数组即可
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
int n = sc.nextInt();
String[] arr = new String[n];
for(int i=0;i<n;i++){
String str = sc.next();
arr[i] = str;
}
Arrays.sort(arr);
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
7. 购物单 (TODO 难一点)
题目描述
王强今天很开心,公司发给N元的年终奖。王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 | 附件 |
---|---|
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。王强想买的东西很多,为了不超出预算,他把每件物品规定了一个重要度,分为 5 等:用整数 1 ~ 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j 件物品的价格为 v[j] ,重要度为 w[j] ,共选中了 k 件物品,编号依次为 j 1 , j 2 ,……, j k ,则所求的总和为:
v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 为乘号)
请你帮助王强设计一个满足要求的购物单。
输入描述:
输入的第 1 行,为两个正整数,用一个空格隔开:N m
(其中 N ( <32000 )表示总钱数, m ( <60 )为希望购买物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
输出描述:
输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值( <200000 )。
输入例子:
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
输出例子:
2200
思路1:给定购物单,逐级增加金额和件数,直到达到购物单上限止(商品件数或者总金额)
参考URL: https://my.oschina.net/u/2822116/blog/822770
思路:给定购物单,逐级增加金额和件数,直到达到购物单上限止(商品件数或者总金额);逐级计算出所对应的最大乘积值,并在此过程中,比较出到此级为止的乘积最大值
8. 简单密码破解
题目描述
密码是我们生活中非常重要的东东,我们的那么一点不能说的秘密就全靠它了。哇哈哈. 接下来渊子要在密码之上再加一套密码,虽然简单但也安全。
假设渊子原来一个BBS上的密码为zvbo9441987,为了方便记忆,他通过一种算法把这个密码变换成YUANzhi1987,这个密码是他的名字和出生年份,怎么忘都忘不了,而且可以明目张胆地放在显眼的地方而不被别人知道真正的密码。
他是这么变换的,大家都知道手机上的字母: 1–1, abc–2, def–3, ghi–4, jkl–5, mno-- 6, pqrs–7, tuv–8 wxyz–9, 0–0,就这么简单,把密码中出现的小写字母都变成对应的数字,数字和其他的符号都不做变换,
声明:密码中没有空格,而密码中出现的大写字母则变成小写之后往后移一位,如:X,先变成小写,再往后移一位,不就是y了嘛,简单吧。记住,z往后移是a哦。
输入描述:
输入包括多个测试数据。输入是一个明文,密码长度不超过100个字符,输入直到文件结尾
输出描述:
输出渊子真正的密文
输入例子:
YUANzhi1987
输出例子:
zvbo9441987
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String pwd = sc.next();
System.out.println(crackpwd(pwd));
}
}
public static String crackpwd(String s) {
StringBuffer sb = new StringBuffer(s);
for (int i = 0; i < sb.length(); i++) {
if ('A' <= sb.charAt(i) && sb.charAt(i) <= 'Z') {
if (sb.charAt(i) == 'Z') {
sb.setCharAt(i, 'a');
} else
sb.setCharAt(i,
Character.toLowerCase((char) (sb.charAt(i) + 1)));
} else if ('a' <= sb.charAt(i) && sb.charAt(i) <= 'z') {
if (String.valueOf(sb.charAt(i)).matches("[abc]")) {
sb.setCharAt(i, '2');
}
if (String.valueOf(sb.charAt(i)).matches("[def]")) {
sb.setCharAt(i, '3');
}
if (String.valueOf(sb.charAt(i)).matches("[ghi]")) {
sb.setCharAt(i, '4');
}
if (String.valueOf(sb.charAt(i)).matches("[jkl]")) {
sb.setCharAt(i, '5');
}
if (String.valueOf(sb.charAt(i)).matches("[mno]")) {
sb.setCharAt(i, '6');
}
if (String.valueOf(sb.charAt(i)).matches("[pqrs]")) {
sb.setCharAt(i, '7');
}
if (String.valueOf(sb.charAt(i)).matches("[tuv]")) {
sb.setCharAt(i, '8');
}
if (String.valueOf(sb.charAt(i)).matches("[wxyz]")) {
sb.setCharAt(i, '9');
}
}
}
return sb.toString();
}
}
推荐练习,掌握 cahrAt,字符转换等常见操作。
9. 坐标移动
题目描述
开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从输入字符串里面读取一些坐标,并将最终输入结果输出到输出文件里面。
输入:
合法坐标为A(或者D或者W或者S) + 数字(两位以内)
坐标之间以;分隔。
非法坐标点需要进行丢弃。如AA10; A1A; % ; YAD; 等。
下面是一个简单的例子 如:
A10;S20;W10;D30;X;A1A;B10A11;;A10;
处理过程:
起点(0,0)
-
A10 = (-10,0)
-
S20 = (-10,-20)
-
W10 = (-10,-10)
-
D30 = (20,-10)
-
x = 无效
-
A1A = 无效
-
B10A11 = 无效
-
一个空 不影响
-
A10 = (10,-10)
结果 (10, -10)
注意请处理多组输入输出
输入描述:
一行字符串
输出描述:
最终坐标,以逗号分隔
示例1
输入
复制
A10;S20;W10;D30;X;A1A;B10A11;;A10;
输出
复制
10,-10
思路:根据 ‘;’ 分割指令,然后判断是不是合法的,合法的指令长度 是 2或者3,同时从下标1开始的字符必须是 数字 0-9.
思路:根据 ‘;’ 分割指令,然后判断是不是合法的,合法的指令长度 是 2或者3,同时从下标1开始的字符必须是 数字 0-9.
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
String s = sc.nextLine();
String[] a = s.split(";");
int x=0;
int y=0;
for(int i = 0;i<a.length;i++){
if(a[i].charAt(0)=='A'&&a[i].substring(1).matches("[0-9]{1,2}")){
x-= Integer.parseInt(a[i].substring(1));
}
if(a[i].charAt(0)=='S'&&a[i].substring(1).matches("[0-9]{1,2}")){
y-=Integer.parseInt(a[i].substring(1));
}
if(a[i].charAt(0)=='W'&&a[i].substring(1).matches("[0-9]{1,2}")){
y+=Integer.parseInt(a[i].substring(1));
}
if(a[i].charAt(0)=='D'&&a[i].substring(1).matches("[0-9]{1,2}")){
x+=Integer.parseInt(a[i].substring(1));
}
}
System.out.println(x+","+y);
}
}
}
总结:根据提意,根据正则判断输入坐标合法性。
10. 求解立方根
题目描述
计算一个数字的立方根,不使用库函数。
保留一位小数。
输入描述:
待求解参数,为double类型(一个实数)
输出描述:
输入参数的立方根。保留一位小数。
示例1
输入
216
输出
6.0
总结:TODO掌握!
思路1:采用牛顿迭代法
Java实现牛顿迭代法求解平方根、立方根
参考URL: https://www.cnblogs.com/ryelqy/p/10842794.html
思路:不使用Math.pow()方法,采用牛顿迭代法,这里可能需要理解一下牛顿迭代过程。
需要掌握数学 牛顿迭代法。
牛顿迭代法。设f(x)=x 3-y, 求f(x)=0时的解x,即为y的立方根。
根据牛顿迭代思想,x n+1=x n-f(x n)/f’(x n)即x=x-(x 3-y)/(3x 2)=(2x+y/x/x)/3;
// 牛顿迭代法
public static double getLiFangGen(double num) {
if (num == 0) {
return num;
}
double num1,num2;
num1 = num;
num2 = (2*num1/3)+(num/(num1*num1*3));//利用牛顿迭代法求解
while(Math.abs(num2-num1)>0.000001){
num1=num2;
num2=(2*num1/3)+(num/(num1*num1*3));
}
DecimalFormat df = new DecimalFormat("#.0");
return Double.parseDouble(df.format(num2));
}
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
double input = sc.nextDouble();
System.out.println(getLiFangGen(input));
}
思路2:二分法
今天开始学Java 计算一个数字的立方根,不使用库函数
参考URL: https://www.it610.com/article/1292831736723480576.htm
11. 字符统计
题目描述
输入一个只包含小写英文字母和数字的字符串,按照不同字符统计个数由多到少输出统计结果,如果统计的个数相同,则按照ASCII码由小到大排序输出。
本题含有多组样例输入
输入描述:
一个只包含小写英文字母和数字的字符串。
输出描述:
一个字符串,为不同字母出现次数的降序表示。若出现次数相同,则按ASCII码的升序输出。
思路: 使用一个hash表(unordered_map)存储 <字符, 出现次数> 的键值对, 然后按出现次数count进行排序(C++算法库中的sort函数), 排序时注意当统计的个数相同时, 按照ASII码由小到大顺序输出.
解本题总共分三步
- 将字符串转换成字符数组。创建字符ascll码对应的整型数组,该数组长度必须大于128,字符的ascll码值就是该数组的下标,遍历字符数组,字符每出现一次对应ascll下标的整数就加一。
- 找出该整型数组的最大值。
- 在整型数组中匹配max,找到则将该整数下标对应的字符加入可变字符序列,max自减直至max为零。(本题无需考虑字符个数相同的情况,因为字符ascll码对应的整型数组本来就是排好的)
字符对应ascll码值下标元素自增来统计数量,使用 (int) i 强转 char i 到对应ascii码。
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
System.out.println(count(sc.nextLine()));
}
}
public static String count(String str) {
char[] strArray = str.toCharArray();
int[] chArray = new int[129];
//字符对应ascll码值下标元素自增来统计数量
for (char i : strArray){
chArray[(int) i]++;
}
int max = 0;
//找出字符数量最多的ascll码值
for (int i = 0; i < chArray.length; i++){
if (max < chArray[i]){
max = chArray[i];
}
}
StringBuilder sb = new StringBuilder();
//按数量从大到小添加到可变字符序列sb
while (max != 0) {
for (int i = 0; i < chArray.length; i++){
if (chArray[i] == max){
sb.append((char) i);
}
}
max--;
}
return sb.toString();
}
基础,必须掌握!
11. 扑克牌大小
题目描述
扑克牌游戏大家应该都比较熟悉了,一副牌由54张组成,含3~A、2各4张,小王1张,大王1张。牌面从小到大用如下字符和字符串表示(其中,小写joker表示小王,大写JOKER表示大王):
3 4 5 6 7 8 9 10 J Q K A 2 joker JOKER
输入两手牌,两手牌之间用"-“连接,每手牌的每张牌以空格分隔,”-"两边没有空格,如:4 4 4 4-joker JOKER。
请比较两手牌大小,输出较大的牌,如果不存在比较关系则输出ERROR。
基本规则:
(1)输入每手牌可能是个子、对子、顺子(连续5张)、三个、炸弹(四个)和对王中的一种,不存在其他情况,由输入保证两手牌都是合法的,顺子已经从小到大排列;
(2)除了炸弹和对王可以和所有牌比较之外,其他类型的牌只能跟相同类型的存在比较关系(如,对子跟对子比较,三个跟三个比较),不考虑拆牌情况(如:将对子拆分成个子);
(3)大小规则跟大家平时了解的常见规则相同,个子、对子、三个比较牌面大小;顺子比较最小牌大小;炸弹大于前面所有的牌,炸弹之间比较牌面大小;对王是最大的牌;
(4)输入的两手牌不会出现相等的情况。
思路1: split 分隔,根据牌面情况分类比较
扑克牌大小
参考URL: https://cloud.tencent.com/developer/article/1617249
不要被这个题吓到,灵活运用split 分隔比较即可,根据题干,比较的情况就那么几种。
import java.util.Scanner;
public class Main {
static String resultline;
public static int count(String str){
return "345678910JQKA2jokerJOKER".indexOf(str);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner in = new Scanner(System.in);
while(in.hasNext()){
String str = in.nextLine();
if ( str.contains("joker JOKER")){
resultline = "joker JOKER";
}else{
String[] split = str.split("-");
String[] left = split[0].split(" ");
String[] right = split[1].split(" ");
if ( left.length == 4 && right.length != 4){
resultline = split[0];
}else if ( left.length != 4 && right.length == 4){
resultline = split[1];
}else if (left.length == right.length){
if ( count(left[0]) > count(right[0])){
resultline = split[0];
}else{
resultline = split[1];
}
}else{
resultline = "ERROR";
}
}
System.out.print(resultline);
}
in.close();
}
}
12. 多线程打印ABCD 【TODO】
题目描述:
- 问题描述:有4个线程和1个公共的字符数组。
- 线程1的功能就是向数组输出A,线程2的功能就是向字符输出B,线程3的功能就是向数组输出C,
- 线程4的功能就是向数组输出D。要求按顺序向数组赋值ABCDABCDABCD,ABCD的个数由线程函数1的参数指定
示例1
输入
10
输出
ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD
思路:用Objcet类中的wait()方法和notify()方法
java实现多线程打印ABCD
参考URL: https://blog.csdn.net/qq_40976527/article/details/105193536
实现思路:
让A线程分配对象锁a,b。
让B线程分配对象锁b,c。
让C线程分配对象锁c,d。
让D线程分配对象锁d,a。
接下就是重点,我们会用Objcet类中的wait()方法和notify()方法。
wait():等待,如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify()方法才能唤醒。wait()方法执行后,当前线程释放锁,其他线程可以竞争该锁。
notify()方法执行后,唤醒线程不会立刻释放锁,要等唤醒线程全部执行完毕后才释放对象锁。
我们要做的就是在A线程在执行打印“A”后,就唤醒B线程,然后让A线程进入等待。以此类推,B线程在执行打印“B”后,就唤醒C线程,然后让B线程进入等待。C线程在执行打印“C”后,就唤醒D线程,然后让C线程进入等待。D线程在执行打印“D”后,就唤醒A线程,然后让D线程进入等待。
这里有个问题我们在启动线程的时候为了确保A线程先打印,我们在初始化线程的时候在线程中增加了两个静态变量newIndex,runIndex。(静态变量的值是所有线程共享的)给线程分别制定相对应的id。id = newIndex++线程执行之后runIndex++。初始化A线程id为0,runIndex为0,B线程为1,runIndex为1,C线程为2,runIndex为2,D线程为3,runIndex为3,当id > runIndex 时说明这个线程是首次执行且还不应该到它执行打印,所以把它在外层synchronized的对象上休眠等待它上一个线程执行答应后唤醒它。
在判断此次打印不是最后一次后则让该线程进入等待。