目录
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
Java创建线程的3种方式
-
extends Thread
-
implements Runnable
-
implements Callable<>
private static class UserThread extends Thread{
@Override
public void run() {
System.out.println("this is UserThread");
}
}
private static class UserRunnable implements Runnable{
@Override
public void run() {
System.out.println("this is UserRunnable");
}
}
private static class UserCallable implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("this is UserRunnable");
return "callable result";
}
}
使用方式
public static void main(String[] args) throws ExecutionException, InterruptedException {
UserThread thread = new UserThread();
thread.start();
Thread MyRunnable = new Thread(new UserRunnable());
MyRunnable.start();
//FutureTask implements Runnable
FutureTask<String> futureTask = new FutureTask<>(new UserCallable());
Thread MyCallable = new Thread(futureTask);
MyCallable.start();
//get方法为阻塞方法,等待结果返回
System.out.println(futureTask.get());
}
线程中断
中断不是停止线程,中断的作用是将线程从sleep阻塞状态 转换成 就绪状态.
-
interrupt() 中断该线程
-
isInterrupted() 判断该线程是否处于中断状态
- static 方法 interrupted() 将中断状态修改为false
使用方式
private static class MyThread extends Thread {
@Override
public void run() {
//获取当前线程名称
String threadName = Thread.currentThread().getName();
while (!isInterrupted()) {//isInterrupted()判断该线程是否处于中断状态
System.out.println(threadName + "is running");
}
System.out.println(threadName + " Interrupt flag is " + isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
//中断该线程
thread.interrupt();
}
运行结果:
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 Interrupt flag is true
常用方法和状态
线程优先级
范围:1~10,默认为5,10为最高值(不可靠,跟操作系统有关,有的设置则不会起作用)
thread.setPriority(5);
守护线程
与主线程共死,finally不能保证一定执行。
使用方式
public class DaemonThread {
private static class MyThread extends Thread{
@Override
public void run() {
try {
//获取当前线程名称
String threadName = Thread.currentThread().getName();
while (!isInterrupted()) {//isInterrupted()判断该线程是否处于中断状态
System.out.println(threadName + " is running");
}
System.out.println(threadName + " Interrupt flag is " + isInterrupted());
}finally {
System.out.println("...........finally");
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
//设置thread为main线程的守护线程,当main线程结束时thread也会随即结束
thread.setDaemon(true);
thread.start();
Thread.sleep(5);
}
}
运行结果:
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
Thread-0 is running
注意:finally代码块没有执行。
synchronized
- 对象锁
- 类锁
//对象锁
public synchronized String sync1(){
return "";
}
//类锁
public static synchronized String sync2(){
return "";
}
对象锁: 同一个对象的所有 synchronized方法(非static synchronized方法)公用一把锁(互斥)
类锁:类class的锁
注意:synchronized方法 和 static synchronized方法同时被调用时,不会出现互斥现象
volatile
只能保证可见性,不能保证原子性.
适用于:只有一个线程写,多个线程读的场景
不能保证原子性 --- 例子:volatile的变量,被多个线程同时引用该值,并且分部改变其值
public class VolatileDemo {
private volatile int count = 10;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
private static class MyThread extends Thread {
private VolatileDemo volatileDemo;
public MyThread(VolatileDemo volatileDemo) {
this.volatileDemo = volatileDemo;
}
@Override
public void run() {
try {
String threadName = Thread.currentThread().getName();
int count = volatileDemo.getCount();
System.out.println(threadName + "-count:" + count);
//休眠2秒后在更新count值
Thread.sleep(2000);
volatileDemo.setCount(count + 10);
System.out.println(threadName + "-count:" + volatileDemo.getCount());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static class MyThread2 extends Thread {
private VolatileDemo volatileDemo;
public MyThread2(VolatileDemo volatileDemo) {
this.volatileDemo = volatileDemo;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
int count = volatileDemo.getCount();
System.out.println(threadName + "-count:" + count);
volatileDemo.setCount(count + 30);
System.out.println(threadName + "-count:" + volatileDemo.getCount());
}
}
public static void main(String[] args) {
VolatileDemo demo = new VolatileDemo();
MyThread thread = new MyThread(demo);
MyThread2 thread2 = new MyThread2(demo);
thread.start();
thread2.start();
}
}
运行结果:
Thread-0-count:10
Thread-1-count:10
Thread-1-count:40
Thread-0-count:20
根据结果可以发现,在Thread1修改count值后,count值为40(此时count = 40已经处于可见状态),但是Thread在Thread1改变之前已经获取到count = 10,经过修改变成count = 20.根据此示例可以看出volatile不满足原子性。
ThreadLocal<>
ThreadLocal是用来提供线程内部的共享变量,在多线程环境下,可以保证各个线程之间的变量互相隔离、相互独立.
使用方式
public class ThreadLocalDemo {
static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
static class MyThread extends Thread{
private Integer id;
public MyThread(Integer id){
this.id = id;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Integer result = threadLocal.get() + id;
threadLocal.set(result);
System.out.println(threadName + ": threalocal value is "+ threadLocal.get());
threadLocal.remove();
}
}
public static void main(String[] args) {
MyThread thread1 = new MyThread(1);
MyThread thread2 = new MyThread(2);
MyThread thread3 = new MyThread(3);
thread1.start();
thread2.start();
thread3.start();
}
}
运行结果:
Thread-0: threalocal value is 2
Thread-2: threalocal value is 4
Thread-1: threalocal value is 3
wait & notify & notifyAll
Object的wait方法可以将其置成阻塞状态,需要调用notify 或者 notifyAll将其唤醒
建议:唤醒建议使用notifyAll,如果使用notify,可能会导致唤醒型号丢失。
例如:
1.使用notify
同一个对象有多个线程的处理逻辑处于wait状态,当使用该对象的notify时,只会唤醒其中一个wait线程,可能唤醒的线程不是我们需要的wait线程。
public class WaitNotifyAllDemo {
private final static String CITY = "XianYang";
private String site;
private Integer km;
public WaitNotifyAllDemo() {
}
public WaitNotifyAllDemo(String site, Integer km) {
this.site = site;
this.km = km;
}
public synchronized void waitKm() {
while (this.km <= 100) {
try {
this.wait();
System.out.println("check km Thread[" + Thread.currentThread().getId() + "] is notified");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("the km is" + this.km + ",I will change db.");
}
public synchronized void waitSite() {
while (CITY.equals(this.site)) {
try {
this.wait();
System.out.println("check site Thread[" + Thread.currentThread().getId() + "] is notified");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("the km is" + this.site + ",I will change db.");
}
public synchronized void changeSite() {
this.site = "Beijing";
this.notify();
}
public synchronized void changeKm() {
this.km = 101;
this.notifyAll();
}
}
public class TestWaitNotifyDemo {
private static WaitNotifyAllDemo demo = new WaitNotifyAllDemo("XianYang", 100);
private static class CheckKM extends Thread {
@Override
public void run() {
demo.waitKm();
}
}
private static class CheckSite extends Thread {
@Override
public void run() {
demo.waitSite();
}
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new CheckKM().start();
}
for (int i = 0; i < 3; i++) {
new CheckSite().start();
}
Thread.sleep(1000);
demo.changeSite();
}
}
运行结果:我们改变的site,却唤醒了km的等待线程,导致唤醒型号丢失
check km Thread[11] is notified
2.notifyAll
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
new CheckKM().start();
}
for (int i = 0; i < 3; i++) {
new CheckSite().start();
}
Thread.sleep(1000);
demo.changeKm();
}
运行结果:由运行结果可以发现唤醒了该对象所有处于wait线程
check site Thread[16] is notified
check km Thread[12] is notified
the km is101,I will change db.
check site Thread[14] is notified
check site Thread[15] is notified
check km Thread[13] is notified
the km is101,I will change db.
check km Thread[11] is notified
the km is101,I will change db.
wait/notifyAll 简单实现连接池
public class MyConnection implements Connection {
public static MyConnection getInstance(){
return new MyConnection();
}
//省略需要重写Connection方法
}
public class MyPool {
//自定义容器
private static LinkedList<Connection> pool = new LinkedList<>();
//初始化
public MyPool(int initialSize) {
if (initialSize > 0) {
for (int i = 0; i < initialSize; i++) {
pool.addLast(new MyConnection());
}
}
}
//获取连接池
public Connection getConnection(long mills) throws InterruptedException {
Connection connection = null;
synchronized (pool) {
if (mills < 0) {
while (pool.isEmpty()) {
pool.wait();
}
connection = pool.removeFirst();
} else {
long overTime = System.currentTimeMillis() + mills;
long remainTime = mills;
while (pool.isEmpty() && remainTime > 0) {
pool.wait(remainTime);
remainTime = overTime - System.currentTimeMillis();
}
if (!pool.isEmpty()) {
connection = pool.removeFirst();
}
}
}
return connection;
}
//释放资源到连接池
public void releaseConnection(Connection connection) {
if (connection != null){
synchronized (pool) {
pool.addLast(connection);
pool.notifyAll();
}
}
}
}
public class PoolTest {
static MyPool pool = new MyPool(10);
static CountDownLatch countDownLatch = null;
public static void main(String[] args) throws InterruptedException {
int threadSize = 50;
countDownLatch = new CountDownLatch(threadSize);
int count = 20;//每个线程的操作次数
AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
for (int i = 0; i < threadSize; i++) {
Thread thread = new Thread(new Work(count, got, notGot), "worker_" + i);
thread.start();
}
countDownLatch.await();
System.out.println("总共尝试了: " + (threadSize * count));
System.out.println("拿到连接的次数: " + got);
System.out.println("没能连接的次数: " + notGot);
}
static class Work implements Runnable {
int count;
AtomicInteger got;
AtomicInteger notGot;
public Work(int count, AtomicInteger got, AtomicInteger notGot) {
this.count = count;
this.got = got;
this.notGot = notGot;
}
@Override
public void run() {
while (count > 0) {
try {
Connection connection = pool.getConnection(1000);
if (connection != null) {
try {
connection.createStatement();
connection.commit();
} finally {
pool.releaseConnection(connection);
got.incrementAndGet();
}
} else {
notGot.incrementAndGet();
System.out.println(Thread.currentThread().getName()
+ "等待超时!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
count--;
}
}
countDownLatch.countDown();
}
}
}
Join
面试点:
线程A如何在线程B之后执行?
线程A执行了线程B的join方法,线程A必须要等待B执行完成之后,线程A才能继续自己的工作。
public class JoinThreadDemo {
static class JoinThread implements Runnable {
Thread previous;
public JoinThread(Thread previous) {
this.previous = previous;
}
@Override
public void run() {
try {
previous.join();
System.out.println(Thread.currentThread().getName() + " is end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread current = Thread.currentThread();
for (int i = 0; i < 10; i++) {
//每一个线程调用前一个线程的join方法
Thread thread = new Thread(new JoinThread(current), i + "");
thread.start();
current = thread;
}
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " is end");
}
}
运行结果:
main is end
0 is end
1 is end
2 is end
3 is end
4 is end
5 is end
6 is end
7 is end
8 is end
9 is end
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
面试点
线程在执行yield()以后,持有的锁是不释放的
sleep()方法被调用以后,持有的锁是不释放的
调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁
调动方法之前,必须要持有锁,调用notify()/notifyAll()方法本身不会释放锁的(所以一般在方法末尾在调用)