了解线程
线程的启动方式
类Thread
接口 Runnable
接口 Callable
Thread 、Runnable 、Callable
Thread 简单代码
public class ThreadDemo {
public static void main(String[] args) {
new Thread(() -> {
System.out.println("thread is started");
}).start();
}
}
这是Thread构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
Runnable 函数式接口 JAVA8提出来新特性面向函数式编程
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Callable的使用
代码编写
FutureTask<String> task = new FutureTask( () -> {
return "call";
});
new Thread(task).start();
// 他会一直阻塞等到拿到接口才释放
System.out.println(task.get());
看看Callable接口
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
总结:Runnable 与 Callable的最重要的区别在于前者拿不到返回值后者可以拿到返回值
线程的状态切换
根据上面这个图我们可以分析到线程的
Thread类部分的方法
// 线程的运行
start
// 线程的中断
interrupt
interrupted
isInterrupted
// 线程的挂起
suspend
// 线程停止
resume
destroy
stop
// ... 直接退出
exit
// 后台进程
isDaemon
setDaemon
中断线程
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 2; i++) {
Thread run = new Thread(() -> {
// 获取线程的名字
String currentThread = Thread.currentThread().getName();
// 判断当前线程的状态
while (!Thread.currentThread().isInterrupted()) {
System.out.println("run");
}
System.out.println(currentThread + "is Running!");
});
threads.add(run);
}
threads.get(0).start();
Thread.sleep(20);
threads.get(0).interrupt();
}
}
总结:
1、run()和start() :run方法就是普通对象的普通方法,只有调用了start()后才会启动线程。
2、yield() :让出cpu的执行权,将线程从运行转到可运行状态,等待cpu调度
3、守护线程与主线程同生命。
4、join方法 可以确保线程的顺序执行。
线程间的共享
synchronized内置锁
对象锁,锁的是类的对象实例。类锁 ,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。
锁对象
在这里插入代码片
锁类
public class ClassLock {
// 定义类
private static class SynClass extends Thread{
@Override
public void run() {
System.out.println("TestClass is running...");
try {
synClass();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//
private static synchronized void synClass() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
System.out.println("synClass going...");
TimeUnit.SECONDS.sleep(1);
System.out.println("synClass end");
}
public static void main(String[] args) {
for (int i = 0 ; i< 10; i++){
new SynClass().start();
}
}
}
锁对象
//使用对象锁的线程
private static class Instance2Syn implements Runnable{
// 传入对象
private SynClzAndInst synClzAndInst;
public Instance2Syn(SynClzAndInst synClzAndInst) {
this.synClzAndInst = synClzAndInst;
}
@Override
public void run() {
System.out.println("TestInstance2 is running..."+synClzAndInst);
synClzAndInst.instance2();
}
}
总结: 在锁类还是锁对象都是比较锁的头的标志位,如果获取到该锁就会执行,如果获取不到锁就会升级。
在jvm章节我。我介绍了锁机制
volatile关键字
public class VolatileUnsafe {
private static class VolatileVar implements Runnable {
private volatile int a = 0;
@Override
public void run() {
String threadName = Thread.currentThread().getName();
a = a++;
System.out.println(threadName+":======"+a);
SleepTools.ms(100);
a = a+1;
System.out.println(threadName+":======"+a);
}
}
public static void main(String[] args) {
VolatileVar v = new VolatileVar();
Thread t1 = new Thread(v);
Thread t2 = new Thread(v);
Thread t3 = new Thread(v);
Thread t4 = new Thread(v);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
// result
Thread-0:======0
Thread-1:======0
Thread-2:======0
Thread-3:======0
Thread-3:======1
Thread-0:======2
Thread-1:======3
Thread-2:======4
这个说明在高并发的时候不能保证原子性,只能说明可见性
ThreadLocal
锁的是当前线程。
面试重点:
- 线程在执行yield()以后,持有的锁是不释放
- sleep()方法被调用以后,持有的锁是不释放
- 调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁
- 调动方法之前,必须要持有锁,调用notify()方法本身不会释放锁的
线程池实现(扩展)
- 去数据拿连接的过程
- 还是去归还的过程
- 产生连接的过程
实战代码实现
public class DBPool {
//数据库池的容器
private static LinkedList<Connection> pool = new LinkedList<Connection>();
// 初始化线程
public DBPool(int initalSize) {
if(initalSize>0) {
for(int i=0;i<initalSize;i++) {
pool.addLast(SqlConnectImpl.fetchConnection());
}
}
}
//在mills时间内还拿不到数据库连接,返回一个null
public Connection fetchConn(long mills) throws InterruptedException {
synchronized (pool) {
if (mills<0) {
while(pool.isEmpty()) {
pool.wait();
}
return pool.removeFirst();
}else {
long overtime = System.currentTimeMillis()+mills;
long remain = mills;
while(pool.isEmpty()&&remain>0) {
pool.wait(remain);
remain = overtime - System.currentTimeMillis();
}
Connection result = null;
// 在非空的条件下
if(!pool.isEmpty()) {
result = pool.removeFirst();
}
return result;
}
}
}
//放回数据库连接
public void releaseConn(Connection conn) {
if(conn!=null) {
synchronized (pool) {
pool.addLast(conn);
pool.notifyAll();
}
}
}
}
package com.xiangxue.ch1.pool;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import com.xiangxue.tools.SleepTools;
public class SqlConnectImpl implements Connection{
/*拿一个数据库连接*/
public static final Connection fetchConnection(){
return new SqlConnectImpl();
}
//TODO
}
测试代码
public class DBPoolTest {
static DBPool pool = new DBPool(10);
// 控制器:控制main线程将会等待所有Woker结束后才能继续执行
static CountDownLatch end;
public static void main(String[] args) throws Exception {
// 线程数量
int threadCount = 50;
end = new CountDownLatch(threadCount);
int count = 20;//每个线程的操作次数
AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new Worker(count, got, notGot),
"worker_"+i);
thread.start();
}
end.await();// main线程在此处等待
System.out.println("总共尝试了: " + (threadCount * count));
System.out.println("拿到连接的次数: " + got);
System.out.println("没能连接的次数: " + notGot);
}
static class Worker implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public Worker(int count, AtomicInteger got,
AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
public void run() {
while (count > 0) {
try {
// 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
// 分别统计连接获取的数量got和未获取到的数量notGot
Connection connection = pool.fetchConn(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConn(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+"等待超时!");
}
} catch (Exception ex) {
} finally {
count--;
}
}
end.countDown();
}
}
}