1、现有的程序代码模拟产生了16个日志对象,并且需要运行16秒才能打印完这些日志,请在程序中增加4个线程去调用parseLog()方法来分头打印这16个日志对象,程序只需要运行4秒即可打印完这些日志对象。
原始代码如下:
public class Test {
public static void main(String[] args){
System.out.println("begin:"+(System.currentTimeMillis()/1000));
/*模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
修改程序代码,开四个线程让这16个对象在4秒钟打完。
*/
for(int i=0;i<16;i++){ //这行代码不能改动
final String log = ""+(i+1);//这行代码不能改动
{
Test.parseLog(log);
}
}
}
//parseLog方法内部的代码不能改动
public static void parseLog(String log){
System.out.println(log+":"+(System.currentTimeMillis()/1000));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
修改后的16个线程4秒内打印出来:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class Test {
public static void main(String[] args){
final BlockingQueue<String> queue=new ArrayBlockingQueue<String>(16);
for(int i=0;i<4;i++){
new Thread(new Runnable() {
public void run() {
while(true){
try {
String log = queue.take();
parseLog(log);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
System.out.println("begin:"+(System.currentTimeMillis()/1000));
/*模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
修改程序代码,开四个线程让这16个对象在4秒钟打完。
*/
for(int i=0;i<16;i++){ //这行代码不能改动
final String log = ""+(i+1);//这行代码不能改动
{
try {
queue.put(log);
} catch (InterruptedException e) {
e.printStackTrace();
}
//Test.parseLog(log);
}
}
}
//parseLog方法内部的代码不能改动
public static void parseLog(String log){
System.out.println(log+":"+(System.currentTimeMillis()/1000));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果为:
begin:1526259813
2:1526259813
3:1526259813
4:1526259813
1:1526259813
5:1526259814
6:1526259814
8:1526259814
7:1526259814
10:1526259815
12:1526259815
11:1526259815
9:1526259815
15:1526259816
13:1526259816
14:1526259816
16:1526259816
2、现成程序中的Test1类中的代码在不断地产生数据,然后交给TestDo.doSome()方法去处理,就好像生产者在不断地产生数据,消费者在不断消费数据。请将程序改造成有10个线程来消费生成者产生的数据,这些消费者都调用TestDo.doSome()方法去进行处理,故每个消费者都需要一秒才能处理完,程序应保证这些消费者线程依次有序地消费数据,只有上一个消费者消费完后,下一个消费者才能消费数据,下一个消费者是谁都可以,但要保证这些消费者线程拿到的数据是有顺序的。
原始代码如下:
public class Test1 {
public static void main(String[] args) {
System.out.println("begin:"+(System.currentTimeMillis()/1000));
for(int i=0;i<10;i++){ //这行不能改动
String input = i+""; //这行不能改动
String output = TestDo.doSome(input);
System.out.println(Thread.currentThread().getName()+ ":" + output);
}
}
}
//不能改动此TestDo类
class TestDo {
public static String doSome(String input){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String output = input + ":"+ (System.currentTimeMillis() / 1000);
return output;
}
}
修改后满足条件的是:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.SynchronousQueue;
public class Test1 {
public static void main(String[] args) {
final Semaphore semaphore=new Semaphore(1);
final SynchronousQueue<String> queue=new SynchronousQueue<String>();
for(int i=0;i<10;i++){
new Thread(new Runnable() {
public void run() {
try {
semaphore.acquire();
String input=queue.take();
String output = TestDo.doSome(input);
System.out.println(Thread.currentThread().getName()+ ":" + output);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
System.out.println("begin:"+(System.currentTimeMillis()/1000));
for(int i=0;i<10;i++){ //这行不能改动
String input = i+""; //这行不能改动
try {
queue.put(input);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//不能改动此TestDo类
class TestDo {
public static String doSome(String input){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String output = input + ":"+ (System.currentTimeMillis() / 1000);
return output;
}
}
运行的结果为:
注:begin:1526262983
Thread-0:0:1526262984
Thread-1:1:1526262985
Thread-2:2:1526262986
Thread-3:3:1526262987
Thread-4:4:1526262988
Thread-6:5:1526262989
Thread-5:6:1526262990
Thread-7:7:1526262991
Thread-8:8:1526262992Thread-9:9:1526262993
1、SynchronousQueue一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。不能在同步队列上进行 peek,因为仅在试图要移除元素时,该元素才存在;除非另一个线程试图移除某个元素,否则也不能(使用任何方法)插入元素;也不能迭代队列,因为其中没有元素可用于迭代。
2、SynchronousQueue是一个特殊队列,即便是空的也不能插入元素,也读不到元素,要往里边插入的时候如果没有读取操作,插入操作就会阻塞,等到有读取操作出现时,插入操作检测到了读取操作,才能把数据插入进去,而读取操作正好可以拿到刚刚插入进去的数据。就好比毒品买卖,我拿着毒品给谁呢,只有买毒品的人来了,才能立马给他,他也拿到了。与Exchanger类似,不过Exchanger是单对单的交换,SynchronousQueue可以多个抢数据,我拿着毒品等人来买,一下来了3个人买,谁抢到了就是谁的;或者我拿3包毒品,3个人同时每人一份。
3、这道题用synchronousQueue的话会一下子将10个数据全打印出来,因为10次循环一次放一个并没有人来取,所以没有放进去,后来一下10个线程来取数据,就一下放进去拿走了。我测试的时候没有这种情况,都是间隔一秒一秒的。测试后发现,将doSome处理后的结果存进去,就会有间隔,而直接存进去,取数据后再处理的话就是一下一片了。分析后知道:put时没有take,10个数据都在等待存入,如果存入的数据是doSome(input)的话,开始取数据时才开始执行doSome所以就会有间隔了。直接存数据,取出后在doSome就是一下拿到10个数据了。
要解决这个问题,可以使用厕所抢坑的方式解决,使用Semaphore来获取许可,每取一次数据释放一次即可。
final Semaphore x = new Semaphore(1); 一次一个
final SynchronousQueue queue = new SynchronousQueue();
每次取数据前都要先获取许可
x.acquire();
取完后释放许可
x.release();
这种方式与使用Lock方式一样
3、现有程序同时启动了4个线程去调用TestDo.doSome(key, value)方法,由于TestDo.doSome(key, value)方法内的代码是先暂停1秒,然后再输出以秒为单位的当前时间值,所以,会打印出4个相同的时间值,如下所示:
4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199615
请修改代码,如果有几个线程调用TestDo.doSome(key, value)方法时,传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果,即当有两个线程的key都是"1"时,它们中的一个要比另外其他线程晚1秒输出结果,如下所示:
4:4:1258199615
1:1:1258199615
3:3:1258199615
1:2:1258199616
总之,当每个线程中指定的key相等时,这些相等key的线程应每隔一秒依次输出时间值(要用互斥),如果key不同,则并行执行(相互之间不互斥)。
原始代码如下:
//不能改动此Test类
public class Test extends Thread{
private TestDo testDo;
private String key;
private String value;
public Test(String key,String key2,String value){
this.testDo = TestDo.getInstance();
/*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象,
以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/
this.key = key+key2;
this.value = value;
}
public static void main(String[] args) throws InterruptedException{
Test a = new Test("1","","1");
Test b = new Test("1","","2");
Test c = new Test("3","","3");
Test d = new Test("4","","4");
System.out.println("begin:"+(System.currentTimeMillis()/1000));
a.start();
b.start();
c.start();
d.start();
}
public void run(){
testDo.doSome(key, value);
}
}
class TestDo {
private TestDo() {}
private static TestDo _instance = new TestDo();
public static TestDo getInstance() {
return _instance;
}
public void doSome(Object key, String value) {
// 以大括号内的是需要局部同步的代码,不能改动!
{
try {
Thread.sleep(1000);
System.out.println(key+":"+value + ":"
+ (System.currentTimeMillis() / 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
修改后满足题上条件的代码如下:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
//不能改动此Test类
public class Test extends Thread{
private TestDo testDo;
private String key;
private String value;
public Test(String key,String key2,String value){
this.testDo = TestDo.getInstance();
/*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象,
以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/
this.key = key+key2;
/* key="1"+"";
key2="1"+"";
*这时key和key2是指向同一个对象,因为编译器在没有翻译成二进制代码之前,就已经优化了,即key="1";key2="1";
*/
this.value = value;
}
public static void main(String[] args) throws InterruptedException{
Test a = new Test("1","","1");
Test b = new Test("1","","2");
Test c = new Test("3","","3");
Test d = new Test("4","","4");
System.out.println("begin:"+(System.currentTimeMillis()/1000));
a.start();
b.start();
c.start();
d.start();
}
public void run(){
testDo.doSome(key, value);
}
}
class TestDo {
private TestDo() {}
private static TestDo _instance = new TestDo();
public static TestDo getInstance() {
return _instance;
}
// private ArrayList<Object> keys=new ArrayList<Object>();
private CopyOnWriteArrayList keys=new CopyOnWriteArrayList();
public void doSome(Object key, String value) {
Object k=key;
if(!keys.contains(k)){
keys.add(k);
}else {
for(Iterator iterator=keys.iterator();iterator.hasNext();){
Object kk=iterator.next();
if(kk.equals(k)){
k=kk;
}
}
}
synchronized (k)
// 以大括号内的是需要局部同步的代码,不能改动!
{
try {
Thread.sleep(1000);
System.out.println(key+":"+value + ":"
+ (System.currentTimeMillis() / 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果为:
begin:1526265233
1:1:1526265234
4:4:1526265234
3:3:1526265234
1:2:1526265235
注:
需要注意:拿原来的锁对象时要迭代锁集合,因为有多个线程在运行,所以迭代时有可能出现其他线程的key没有做过锁,需要将它加到锁集合中,可是这时候这个线程还在迭代过程中,迭代时不能操作集合中的数据,就会发生异常。要解决这个问题,就需要用到同步集合了。CopyOnWriteArrayList
其它:
a = “1”+””;
b = “1”+””;
a和b是同一个对象,常量相加 equals为真 ==为假
Object o1 = new String("1");
Object o2 = new String("1");
System.out.println(o1==o2);//false
System.out.println(o1.equals(o2));//true
System.out.println(o1);//1
System.out.println(o2);//1
Object o3 = "1"+"";
Object o4 = "1"+"";
System.out.println(o3==o4);//true
System.out.println(o3.equals(o4));//true
Object o5 = "2"+"";
Object o6 = get("2","");
System.out.println(o5==o6);//false
System.out.println(o5.equals(o6));//true
System.out.println(o5+"__"+o6);//2__2
public static Object get(String a, String b)
{
return a+b;
}