Java 知识目录
Thread
常用构造方法中,常用的有两个参数 Thread(Runnable target, String name) Runnable
接口的实现类,以及线程名称
/**
* 通过继承 Thread 创建多线程
* 1. 创建一个Thread类的子类
* 2. 在Thread子类中重写Thread类中的run方法,设置线程任务
* 3. 调用Thread类中的start方法,开启新线程,执行run方法
*/
public class ExtendThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("ExtendThread" + i);
}
}
/**
* 运行结果
* ExtendThread0
* main0
* ExtendThread1
* main1
* ExtendThread2
* main2
*/
public static void main(String[] args) {
ExtendThread extendThread = new ExtendThread();
extendThread.start();
for (int i = 0; i < 3; i++) {
System.out.println("main" + i);
}
}
}
/*
* 创建`Runnable` 接口的实现类
* 在实现类中重写 `Runnable` 接口的 `run` 方法,设置线程任务
* 创建`Runnable` 接口的实现类对象
* 创建`Thread`类对象,构造方法中传递`Runnable` 接口的实现类对象
* 调用`Thread` 类中的 `start`方法,开启新的线程执行 `run`方法
*
* */
public class RunnableImpl implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
RunnableImpl runnable = new RunnableImpl();
Thread thread = new Thread(runnable);
thread.start();
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
package functions;
/**
* static Thread currentThread() 返回对当前正在执行的线程对象的引用。
* String getName() 返回此线程的名称。
* void setName(String name) 将此线程的名称更改为等于参数 name 。
* static void sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),使用 Thread 直接调用即可
* void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
*/
public class ThreadFunction implements Runnable{
@Override
public void run() {
System.out.println("ThreadFunction:" + Thread.currentThread().getName());
//延迟100ms
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Runnable runnable = new ThreadFunction();
Thread thread = new Thread(runnable);
//添加了延时函数,所以main函数线程会先执行
thread.start();
System.out.println("main:" + Thread.currentThread().getName());
//修改main函数线程的名称
Thread.currentThread().setName("main2");
System.out.println("main2:" + Thread.currentThread().getName());
}
}
实际开发中一般使用Runnable
接口的方式比较多,因为:
通过继承Thread
类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread
类。而实现Runnable
接口可以避免单继承的局限性。
/**
* Synchronized 修饰一个代码块
* 当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,
* 在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块
* 以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定
* 当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象。
*/
public class SynchronizedBlock implements Runnable{
private static int count = 0;//计数
@Override
public void run() {
synchronized (this){
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + ":" + (count++));
}
}
}
public static void main(String[] args) {
SynchronizedBlock synchronizedBlock = new SynchronizedBlock();
Thread thread1 = new Thread(synchronizedBlock,"SynchronizedBlock1");
Thread thread2 = new Thread(synchronizedBlock,"SynchronizedBlock2");
thread1.start();
thread2.start();
}
}
volatile
关键字是线程同步的轻量级实现,所以volatile
性能肯定比synchronized
关键字要好。但是volatile
关键字只能用于变量而synchronized
关键字可以修饰方法以及代码块。synchronized
关键字 在JavaSE1.6
之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升,实际开发中使用synchronized
关键字的场景还是更多-些。volatile
关键字不会发生阻塞,而synchronized
关键字可能会发生阻塞volatile
关键字能保证数据的可见性,但不能保证数据的原子性。synchronized
关键字两者都能保证。volatile
关键字主要用于解决变量在多个线程之间的可见性,而synchronized
关键字解决的是多个线程之间访问资源的同步性。Atomic
是指一个 操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始, 就不会被其他线程干扰。所以,所谓原子类说简单点就是具有原子/原子操作特征的类。**AtomicInteger **使用示例:
class AtomicIntegerTest {
private AtomicInteger count = new AtomicInteger();
//使⽤AtomicInteger之后,不需要对该⽅法加锁,也可以实现线程安全。
public void increment() {
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
* @Author 安仔夏天很勤奋
* Create Date is 2019/3/21
*
* 描述 Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量。
* 因此,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,
* 它们也无法访问到对方的ThreadLocal变量。
*/
public class ThreadLocalExsample {
/**
* 创建了一个MyRunnable实例,并将该实例作为参数传递给两个线程。两个线程分别执行run()方法,
* 并且都在ThreadLocal实例上保存了不同的值。如果它们访问的不是ThreadLocal对象并且调用的set()方法被同步了,
* 则第二个线程会覆盖掉第一个线程设置的值。但是,由于它们访问的是一个ThreadLocal对象,
* 因此这两个线程都无法看到对方保存的值。也就是说,它们存取的是两个不同的值。
*/
public static class MyRunnable implements Runnable {
/**
* 例化了一个ThreadLocal对象。我们只需要实例化对象一次,并且也不需要知道它是被哪个线程实例化。
* 虽然所有的线程都能访问到这个ThreadLocal实例,但是每个线程却只能访问到自己通过调用ThreadLocal的
* set()方法设置的值。即使是两个不同的线程在同一个ThreadLocal对象上设置了不同的值,
* 他们仍然无法访问到对方的值。
*/
private ThreadLocal threadLocal = new ThreadLocal();
@Override
public void run() {
//一旦创建了一个ThreadLocal变量,你可以通过如下代码设置某个需要保存的值
threadLocal.set((int) (Math.random() * 100D));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
//可以通过下面方法读取保存在ThreadLocal变量中的值
System.out.println("-------threadLocal value-------"+threadLocal.get());
}
}
public static void main(String[] args) {
MyRunnable sharedRunnableInstance = new MyRunnable();
Thread thread1 = new Thread(sharedRunnableInstance);
Thread thread2 = new Thread(sharedRunnableInstance);
thread1.start();
thread2.start();
}
}
运行结果
-------threadLocal value-------38
-------threadLocal value-------88
1.execute()
方 法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
2. submit()
方法用于提交需要返回值的任务。线程池会返回一个Future
类型的对象,通过这个Future
对象可以判断任务是否执行成功,并且可以通过Future
的get()
方法来获取返回值,get()
方法会阻塞当前线程直到任务完成,而使用get (long t imeout, TimeUnit unit)
方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 10;
private static final int QUEUE_CAPACITY = 100;
private static final Long KEEP_ALIVE_TIME = 1L;
public static void main(String[] args) {
//使⽤阿⾥巴巴推荐的创建线程池的⽅式
//通过ThreadPoolExecutor构造函数⾃定义参数创建
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE, //最小可以同时运行的线程数量
MAX_POOL_SIZE, //线程最大连接数
KEEP_ALIVE_TIME, //回收等待时间为1L
TimeUnit.SECONDS, //等待时间单位为分钟
new ArrayBlockingQueue<>(QUEUE_CAPACITY), //任务队列为ArrayBlockingQueue,并且容量为100;
new ThreadPoolExecutor.CallerRunsPolicy());//饱和策略为CallerRunsPolicy
for (int i = 0; i < 10; i++) {
//创建WorkerThread对象(WorkerThread类实现了Runnable 接⼝)
Runnable worker = new MyRunnable();
//执⾏Runnable
executor.execute(worker);
}
//终⽌线程池
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
}
}
synchronized
可以保证代码片段的原子性。volatile
关键字可以保证共享变量的可见性。volatile
关键字可以禁止指令进行重排序优化。参考文献:
https://www.cnblogs.com/fnlingnzb-learner/p/10335662.html
https://www.jianshu.com/p/6fc3bba12f38
https://blog.csdn.net/u010687392/article/details/50549236
https://github.com/Snailclimb/JavaGuide