1. 并发编程
下面的程序需要导入 java.util.concurrent.*
1.1 用户线程 (四种方式)
创建用户线程的方式有4种,分别是 继承 Thread类、实现 Runnable 接口、实现 Callable 接口、以及使用线程池。
1.1.1 继承自 Thread 类
这种方式比较简单,通过继承 Thread 类,并重写 run() 方法。在使用的时候 实例化并调用 start() 方法即可。
public class MyMain {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("继承自Thread创建线程!");
}
}
1.1.2 实现 Runnable 接口
实现 Runnable 接口并重写 run()方法,但由于没有 start() 方法启动线程,所以需要借助 Thread 来启动。
public class MyMain {
public static void main(String[] args) {
Runnable myRunnableImpl = new MyRunnableImpl();
Thread thread = new Thread(myRunnableImpl);
thread.start();
}
}
class MyRunnableImpl implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable创建线程!");
}
}
1.1.3 实现 Callable 接口
通过实现 Callable 接口创建线程。
与 Runnable 接口的不同:
- Callable 需要重写的是 call() 方法
- Callable 有返回值,所以需要借助 FutureTask 这个容器用于获取结果
- 注意 通过 FutureTask 容器获取计算结果的方法 get() 会有异常抛出
public class MyMain {
public static void main(String[] args) {
Callable<String> myCallable = new MyCallableImpl();
FutureTask<String> futureTask = new FutureTask<>(myCallable);
Thread thread = new Thread(futureTask);
thread.start();
try{
String result = futureTask.get();
System.out.println(result);
}catch (Exception e){
e.printStackTrace();
}
}
}
class MyCallableImpl implements Callable<String>{
@Override
public String call(){
System.out.println("实现Callable接口创建线程!");
return "这是 call 方法的返回值!";
}
}
小结
以上三种方式创建线程,最后都是通过 Thread 进行执行。Callable 有结果返回,所以用 FutureTask 容器保存,但最后也还是交由 Thread 执行。
1.4 线程池创建线程
这种方式避免了线程的反复创建和销毁。
首先通过 Executors 工具类的 newFixedThreadPool() 创建线程池(ExecutorsService)
ExecutorsService 内有两个成员方法可以提交线程,分别是 submit() 和 execute()
区别:
- 方法的参数
- submit() 方法 接受的变量可以是 Runnable、Callable、Thread
- execute() 方法 不能接受 Callable
- 返回值
- submit() 方法的返回值为 Future
,可以接受 Callable 的 call 方法的返回值 - execute() 方法无返回值
- submit() 方法的返回值为 Future
最后,线程池使用结束,可以使用 shutDown() 方法关闭线程池
public class MyMain {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(5);
Callable<String> myCallable = new MyCallableImpl();
Runnable myRunnable = new MyRunnableImpl();
Thread myThread = new MyThread();
// submit 可以获取结果
Future<String> result = pool.submit(myCallable);
System.out.println(result.get());
// execute 无返回结果
pool.execute(myRunnable);
pool.execute(myThread);
// 关闭线程池
pool.shutdown();
}
}
1.2 守护线程
守护线程,俺一开始以为它是个很强的玩意,守护嘛。但实际上它对于程序而言似乎并不是那么重要。
守护线程,如果其它用户线程(非守护线程)终止了,那么守护线程也就终止,与守护线程是否执行完成没有关系。所以一般用于执行一些任务、服务,如定期缓存清理等。
因为守护线程随时都可能被中止,不能保证完整性,所以守护线程不用于实现一些关键业务逻辑。而且,守护线程的优先级低,通常低于用户线程,所以,资源优先提供给用户线程。
通过 Thread 的成员方法 setDaemon(true) 可以将线程设置为守护线程。
!值得注意:守护线程需要在用户线程启动后才能启动,否则会发送 IllegalThreaStatusException 异常
public class MyMain {
public static void main(String[] args){
Thread myThread = new MyThread();
myThread.setDaemon(true);
myThread.start();
}
}
2. Java 锁机制
Java 的锁机制,这里将简单介绍 synchronized关键字、ReentrantLock、ReadWriteLock
后两个需要 import java.util.concurrent.locks.*;
2.1 synchronized关键字
synchronized 关键可以修饰方法,也可以作用在对象上。
public class MyMain {
public static void main(String[] args) {
Thread thread = new MyThread();
ExecutorService pool = Executors.newFixedThreadPool(12);
pool.execute(thread);
pool.execute(thread);
pool.execute(thread);
pool.execute(thread);
pool.execute(thread);
pool.shutdown();
}
}
class MyThread extends Thread{
private static Integer num = 10;
@Override
public void run() {
while (sell());
}
private synchronized boolean sell(){
if (num<=0)
return false;
System.out.printf("%s---%d\n",Thread.currentThread().toString(),num);
num -= 1;
return true;
}
}
class MyThread extends Thread{
private static Integer num = 1000;
@Override
public void run() {
while (sell());
}
private boolean sell(){
if (num<0)
return false;
synchronized (num){
System.out.printf("%s---%d\n",Thread.currentThread().toString(),num);
num--;
}
return true;
}
}
2.2 ReentrantLock
使用ReentrantLock,需要在实例化的时候传入 ReentrantLock对象,需要上锁的时候,调用 lock.lock(),解锁的时候使用 lock.unlock() 即可。
public class MyMain {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Runnable myRunnable = new MyRunnableImpl(lock);
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.execute(myRunnable);
pool.execute(myRunnable);
pool.execute(myRunnable);
pool.shutdown();
}
}
class MyRunnableImpl implements Runnable{
private static Integer num = 100;
private final ReentrantLock lock;
MyRunnableImpl(ReentrantLock lock) {
this.lock = lock;
}
@Override
public void run() {
while (sell());
}
private boolean sell(){
lock.lock();
try{
if (num<0)
return false;
System.out.printf("%s---%d\n",Thread.currentThread().toString(),num);
num--;
}finally {
lock.unlock();
}
return true;
}
}
2.3 ReentrantReadWrite
这是读锁和写锁,允许多个线程同时持有读锁,但只允许一个线程持有写锁。
public class MyMain {
public static void main(String[] args) {
ReadWriteLock lock = new ReentrantReadWriteLock();
Callable<String > myCallable = new MyCallableImpl(lock);
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.submit(myCallable);
pool.submit(myCallable);
pool.submit(myCallable);
pool.shutdown();
}
}
class MyCallableImpl implements Callable<String>{
private static Integer num = 100;
private ReadWriteLock lock;
MyCallableImpl(ReadWriteLock lock){
this.lock = lock;
}
@Override
public String call() throws Exception {
while (sell());
return null;
}
public boolean sell(){
lock.readLock().lock();
try {
if (num<0)
return false;
}finally {
lock.readLock().unlock();
}
lock.writeLock().lock();
try{
System.out.printf("%s****%d\n",Thread.currentThread(),num);
num--;
}finally {
lock.writeLock().unlock();
}
return true;
}
}
标签:Java,复习,Thread,lock,知识,Callable,线程,public,pool
From: https://www.cnblogs.com/welcome-to-future/p/17762891.html