编程篇
1.设计一个LinkedHashMap。
2.使用Java编写一个死锁程序,并且能够自动跳出死锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| package com.me.ioc;
import java.util.Date; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit;
public class DeadLockTest {
public static void main(String[] args) { Thread th1 = new Thread(new DeadLock(true)); Thread th2 = new Thread(new DeadLock(false));
th1.setName("线程1"); th2.setName("线程2"); th1.start(); th2.start();
}
static class DeadLock implements Runnable {
private final static Object o1 = new Object(); private final static Object o2 = new Object(); private final static Semaphore a1 = new Semaphore(1); private final static Semaphore a2 = new Semaphore(1);
boolean lockFlag;
DeadLock(boolean lockFlag) { this.lockFlag = lockFlag; }
@Override public void run() { try { if (lockFlag) { if (a1.tryAcquire(1, TimeUnit.SECONDS)) { synchronized (o1) { try { System.out.println(Thread.currentThread().getName() + new Date().toString() + " Lock 锁住 o1"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } if (a2.tryAcquire(1, TimeUnit.SECONDS)) { synchronized (o2) { System.out.println(Thread.currentThread().getName() + new Date().toString() + "Lock 锁住 o2"); } } else { System.out.println(Thread.currentThread().getName() + new Date().toString() + "Lock 锁 o2 失败"); } } } else { System.out.println(Thread.currentThread().getName() + new Date().toString() + "Lock 锁 o1 失败"); } a1.release(); a2.release(); } else { if (a1.tryAcquire(1, TimeUnit.SECONDS)) { synchronized (o2) { try { System.out.println(new Date().toString() + " Lock 锁住 o1"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } if (a2.tryAcquire(1, TimeUnit.SECONDS)) { synchronized (o1) { System.out.println(Thread.currentThread().getName() + new Date().toString() + "Lock 锁住 o2"); } } else { System.out.println(Thread.currentThread().getName() + new Date().toString() + "Lock 锁 o2 失败"); } } } else { System.out.println(Thread.currentThread().getName() + new Date().toString() + "Lock 锁 o1 失败"); } a1.release(); a2.release(); } } catch (Exception e) { e.printStackTrace(); } } } }
|
3.用Java实现读取特定格式的大文件数据写入redis节点,要求速度库、数据准确、redis写入压力可控。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| package com.me.ioc;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Pipeline; import redis.clients.jedis.Response;
import java.util.HashMap; import java.util.Map; import java.util.Set;
public class RedisTest { public static void main(String[] args) { Jedis redis = new Jedis("120.26.137.224", 6379, 400000); Map<String, String> map = new HashMap<>(); redis.select(8);
long start = System.currentTimeMillis(); for (int i = 0; i< 1000000; i ++) { map.clear(); map.put("k_" + i, "v_" + i); redis.hmset("key_" + i, map); } long end = System.currentTimeMillis();
Pipeline pipeline = redis.pipelined(); start = System.currentTimeMillis(); for (int i = 0; i< 1000000; i ++) { map.clear(); map.put("k_" + i, "v_" + i); redis.hmset("key_" + i, map); } pipeline.sync(); end = System.currentTimeMillis(); System.out.println("dbsize:[" + redis.dbSize() + "] .. "); System.out.println("hmset with pipeline used [" + (end - start) / 1000 + "] seconds ..");
Set<String> keys = redis.keys("*");
start = System.currentTimeMillis(); Map<String,Map<String,String>> result = new HashMap<>(); for(String key : keys) { result.put(key, redis.hgetAll(key)); } end = System.currentTimeMillis(); System.out.println("result size:[" + result.size() + "] .."); System.out.println("hgetAll without pipeline used [" + (end - start) / 1000 + "] seconds ..");
Map<String,Response<Map<String,String>>> responses = new HashMap<>(keys.size()); result.clear(); start = System.currentTimeMillis(); for(String key : keys) { responses.put(key, pipeline.hgetAll(key)); } pipeline.sync();
for(String k : responses.keySet()) { result.put(k, responses.get(k).get()); } end = System.currentTimeMillis(); System.out.println("result size:[" + result.size() + "] .."); System.out.println("hgetAll with pipeline used [" + (end - start) / 1000 + "] seconds ..");
redis.disconnect(); } }
|
简答题
4.Java中的volatile关键字的作用。
volatile 关键字作用是,使系统中所有线程对该关键字修饰的变量共享可见,可以禁止线程的工作内存对volatile修饰的变量进行缓存。
http://www.jb51.net/article/93844.htm
5.线程之间如何通信。
同步方式synchorized,即共享变量,synchororized修改加锁的对象上(方法上)。多个线程需要访问同一个共享变量,谁拿到了锁(获得了访问权限),谁就可以执行。
轮询方式。多个线程根据传入的构造参数,轮询判断满足的条件,条件字段用volatile修饰,保证内存可见性。
wait/notify机制,也是synchorized,但是这个用线程上,一般用于run()的实现内部。线程调用wait() 放弃CPU,并进入阻塞状态。—不像②while轮询那样占用CPU。wait(),notify(),notifyAll()都必须使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步 才具有锁。
管道通信就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信。分布式系统中说的两种通信机制:共享内存机制和消息通信机制。感觉前面的①中的synchronized关键字和②中的while轮询 “属于” 共享内存机制,由于是轮询的条件使用了volatile关键字修饰时,这就表示它们通过判断这个“共享的条件变量“是否改变了,来实现进程间的交流。而管道通信,更像消息传递机制,也就是说:通过管道,将一个线程中的消息发送给另一个。
6.为什么wait()、notify()、notifyAll()等方法放在Object类中,而不是Thread类中?
简单说:因为synchronized中的这把锁可以是任意对象,所以任意对象都可以调用wait()和notify();所以wait和notify属于Object。
专业说:因为这些方法在操作同步线程时,都必须要标识它们操作线程的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。
Java的每个对象中都有一个锁(monitor,也可以成为监视器)并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法。
7.简要阐述Java BID和NIO的原理和区别,如果可以的话选择其中一种简单实现客户端、服务端程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| package com.me.ioc;
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Set;
public class NIOServer { public void init() throws IOException { Charset charset = Charset.forName("UTF-8"); Selector selector = Selector.open(); ServerSocketChannel server = ServerSocketChannel.open(); server.socket().bind(new InetSocketAddress(7777), 1024); server.configureBlocking(false); server.register(selector, SelectionKey.OP_ACCEPT);
while (true) { selector.select(1000); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> it = keys.iterator(); SelectionKey key = null; while (it.hasNext()) { key = it.next(); it.remove(); if (key.isAcceptable()) { ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel channel = ssc.accept(); channel.configureBlocking(false); channel.register(selector, SelectionKey.OP_READ); key.interestOps(SelectionKey.OP_ACCEPT);
}
if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); String context = ""; try { int readByte = channel.read(buffer); if (readByte > 0) { buffer.flip(); byte bytes[] = new byte[buffer.remaining()]; buffer.get(bytes); context += new String(bytes); System.out.println(context); doWrite(channel); } key.interestOps(SelectionKey.OP_READ); }catch (Exception e) { key.cancel(); if (key.channel() != null) { key.channel().close(); } } } } }
}
private void doWrite(SocketChannel channel) throws IOException { byte req[] = "服务器已接收".getBytes(); ByteBuffer buffer = ByteBuffer.allocate(req.length); buffer.put(req); buffer.flip(); channel.write(buffer); if (buffer.hasRemaining()) { System.out.println("Send 2 Service successed"); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| package com.me.ioc;
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue;
public class NIOClient { private Selector selector = null; static Charset charset = Charset.forName("UTF-8");
private volatile boolean stop = false; public ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(8); public void init() throws IOException { selector = Selector.open(); SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); if (channel.connect(new InetSocketAddress("127.0.0.1", 7777))) { channel.register(selector, SelectionKey.OP_READ); doWrite(channel, "66666666666"); } else { channel.register(selector, SelectionKey.OP_CONNECT); }
while (!stop){ selector.select(1000); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> it = keys.iterator(); SelectionKey key = null; while (it.hasNext()) { key = it.next(); it.remove(); SocketChannel sc = (SocketChannel) key.channel(); if (key.isConnectable()) { if (channel.finishConnect()) {
sc.register(selector, SelectionKey.OP_READ); doWrite(channel, "7777777777777"); }else { System.exit(1); } }
if (key.isReadable()) { ByteBuffer buffer = ByteBuffer.allocate(1024); int readBytes = sc.read(buffer); String context = ""; if (readBytes > 0) { buffer.flip(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); context += new String(bytes); stop = true; } else if (readBytes < 0) { key.channel(); sc.close(); } System.out.println(context); key.interestOps(SelectionKey.OP_READ); } }
} }
private void doWrite(SocketChannel channel, String data) throws IOException { byte[] req = data.getBytes(); ByteBuffer buffer = ByteBuffer.allocate(req.length); buffer.put(req); buffer.flip(); channel.write(buffer); if (buffer.hasRemaining()) { System.out.println("Send 2 client successed"); } } }
|
8.解释同步和异步、阻塞和非阻塞的区别,并从以下四种IO模型中人选一种阐述其原理,同步阻塞IO、同步非阻塞IO、IO多路复用、异步IO的原理。
同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
当一个异步过程调用发出后,调用者不会立刻得到结果。实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用。
阻塞/非阻塞, 它们是程序在等待消息(无所谓同步或者异步)时的状态.
阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
非阻塞指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
同步阻塞形式:效率是最低的,拿上面的例子来说,就是你专心排队,什么别的事都不做。实际程序中就是未对fd 设置O_NONBLOCK 标志位的read/write 操作,
异步阻塞形式:如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发,也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面;
异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息被触发时被阻塞.比如select 函数,假如传入的最后一个timeout 参数为NULL,那么如果所关注的事件没有一个被触发,程序就会一直阻塞在这个select 调用处.
同步非阻塞形式:实际上是效率低下的,想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的;很多人会写阻塞的read/write 操作,但是别忘了可以对fd 设置O_NONBLOCK 标志位,这样就可以将同步操作变成非阻塞的了;
异步非阻塞形式:效率更高,因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换.
I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流
9.请结合自身实际工作情况,说出三点与Java代码、性能优化有关的案例。
排错题
10.service.java文件:
1 2 3 4 5 6 7 8 9 10 11
| public class Service { private static Service service = null; private static List<Sender> discardList = new ArrayList<Sender>(); private int discardNum = 2; private AtomicInteger count = new AtomicInteger();
public static Service getService() { return null; } }
|
11.GeTui.java文件:
1 2 3 4 5
| public class GeTui { public static void send() { System.out.println("push the world!"); } }
|
TestGeTui.java:
1 2 3 4 5 6
| public class TestGeTui { public static void main(String[] args) { GeTui geTui = null; geTui.send(); } }
|