进程与线程
进程通常指的是一个软件列如QQ.exe等。一个进程里面有多个线程,至少包含一个线程,java中默认有两个线程(main,GC)
java中开启线程三种方式:Thread,Runnable,callable
java不可以开启线程
通过group.add()把当前线程添加到当前线程组,调用本地方法start0(),调用的是底层c++,因为java无法直接操作硬件,java是运行在虚拟机上的。所以需要通过c++完成。
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
并发编程:并发与并行
并发编程的本质:充分利用CPU的资源
并发:多个线程操作同一个资源
一核CPU,模拟多条线程
并线:多个一块行动
多核CPU,模拟多条线程可以同时执行
public static void main(String[] args) {
//获得cpu核数
System.out.println(Runtime.getRuntime().availableProcessors());
}
线程的状态
//线程新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
wait/sleep的区别
- 类不同
- wait是Object类中的,sleep是Threa中的
- 锁的释放
- wait不释放锁,sleep不释放锁
- 使用范围不同
- wait必须在同步代码块中才能使用,sleep不做限制
- wait/sleep都需要捕获异常InterruptedException中断异常
Lock锁
lock的使用
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
lock接口的实现类
ReentrantLock:可重入锁
ReentrantReadWriteLock.ReadLock:读锁
ReentrantReadWriteLock.WriteLock:写锁
ReentrantLock的构造方法:
默认是非公平锁
//非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
synchronized和lock的区别
- synchronized是java中的关键字,lock是一个java类
- synchronized无法判断获得锁的状态,lock可以判断是否获得锁
- synchronized会自动释放锁,lock需要手动释放锁,不释放锁会死锁
- 使用synchronized 线程1获得了锁后面的线程将会等待,线程1要是阻塞后面的线程就会一直等待。lock锁使用tryLock()就不会等待
- synchronized可重入锁,不可以中断,非公平锁,lock可重入锁,可以中断,可以字节设置非公平锁和公平锁
- synchronized适合少量的代码同步问题,lock适合锁大量的同步代码
生成者与消费者
synchronized:
public class Demo01 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.incte();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.derce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data{
private Integer num = 0;
public synchronized void incte() throws InterruptedException{
while(num != 0 ){
this.wait();
}
num ++;
System.out.println(Thread.currentThread().getName()+" -->"+ num);
this.notifyAll();
}
public synchronized void derce() throws InterruptedException{
while(num == 0 ){
this.wait();
}
num --;
System.out.println(Thread.currentThread().getName()+" -->"+ num);
this.notifyAll();
}
}
JUC:
通过lock找到Condition,
Lock
替换synchronized
方法和语句的使用, Condition
取代了对象监视器方法的使用。
代码演示
public class Demo01 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{for (int i = 0; i < 10; i++)data.incte();},"A").start();
new Thread(()->{for (int i = 0; i < 10; i++)data.derce();},"B").start();
new Thread(()->{for (int i = 0; i < 10; i++)data.incte();},"C").start();
new Thread(()->{for (int i = 0; i < 10; i++)data.derce();},"D").start();
}
}
class Data{
private Integer num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void incte(){
lock.lock();
try {
while(num != 0 ){
condition.await();
}
num ++;
System.out.println(Thread.currentThread().getName()+" -->"+ num);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void derce(){
lock.lock();
try {
while(num == 0 ){
condition.await();
}
num --;
;System.out.println(Thread.currentThread().getName()+" -->"+ num);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
Condition实现精准唤醒
public class Demo02 {
public static void main(String[] args) {
DataTwo dataTwo = new DataTwo();
new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinA();},"A").start();
new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinB();},"B").start();
new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinC();},"C").start();
}
}
class DataTwo{
Lock l = new ReentrantLock();
Condition condition = l.newCondition();
private int num = 1;
public void prinA(){
l.lock();
try {
while (num != 1){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"正在执行");
num=2;
condition.signal();
}catch (Exception e){
e.printStackTrace();
}finally {
l.unlock();
}
}
public void prinB(){
l.lock();
try {
while (num != 2){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"正在执行");
num=3;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
public void prinC(){
l.lock();
try {
while(num != 3){
condition.await();
}
System.out.println(Thread.currentThread().getName()+"正在执行");
num = 1;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
l.unlock();
}
}
}
CopyOnWriteArrayList
在并发下ArrayList是线程不安全的,会出现异常java.util.ConcurrentModificationException(并发修改异常)
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,4));
System.out.println(list);
},String.valueOf(i)).start();
}
}
那么怎么解决这个并发修改异常问题呢?
方法一
list集合中Vector集合是线程安全的所以可以使用Vector解决
List<String> list = new Vector<String>();
方法二
可以使用Collections中的synchronizedList()将ArrayLIst改变为线程安全的
List<String> list = Collections.synchronizedList(new ArrayList<>());
方法三
使用CopyOnWriteArrayList
List<String> list = new CopyOnWriteArrayList<>();
使用Vector本事就是线程安全的在添加数据的时候就是同步的,
使用Collections.synchronizedList()就是将传过去的list添加同步锁
使用CopyOnWriteArrayList读写分离,在写入数据的时候复制一份并且在他的底层是使用lock锁
推荐使用CopyOnWriteArrayList因为使用synchronized效率会比较低但。
如果需要将map改变为线程安全需要使用到ConcurrentHash Map()
Map<String,Object> map = new ConcurrentHashMap<String,Object>();
Collable
Callable
接口类似于Runnable,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable
不返回结果,也不能抛出被检查的异常。
Callable与Runnable不同于,Callable可以抛出异常,使用的方法不同Callable使用call方法,Callable可以有返回值
ReadWriteLock(读写锁)
java并发包中ReadWriteLock是一个接口,主要有两个方法
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
实现类:ReadWriteLockView,ReentrantReadWriteLock
readLock是读锁,writeLock是写锁,使用ReadWriteLock可以更加细粒度控制
public class ReadWriteLockTest {
static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public static void main(String[] args) {
Date date = new Date();
Test test = new Test();
new Thread(()->test.start(true,sdf.format(date)),"write-----1").start();//读
new Thread(()->test.start(true,sdf.format(date)),"write-----2").start();//读
new Thread(()->test.start(false,sdf.format(date)),"read-----2").start();//写
new Thread(()->test.start(false,sdf.format(date)),"read-----1").start();//写
new Thread(()->test.start(false,sdf.format(date)),"read-----3").start();//写
}
}
class Test{
ReadWriteLock rel = new ReentrantReadWriteLock();//构建ReentrantReadWriteLock使锁细腻话
//创建读锁
Lock readLock = rel.readLock();
//创建写锁
Lock writeLock = rel.writeLock();
public <T> void start(boolean flse, T t){
if(flse)read(t);else write(t);
}
//读方法
public <T> void read(T t){
readLock.lock();//使用读锁
try {
System.out.println("线程"+Thread.currentThread().getName()+"正在读,当前时间是:"+t);
TimeUnit.SECONDS.sleep(2);//停滞两秒
} catch (Exception e) {
e.printStackTrace();
} finally {
readLock.unlock();//释放读锁
}
}
//写方法
public <T> void write(T t){
writeLock.lock();//使用读锁
try {
System.out.println("线程"+Thread.currentThread().getName()+"正在写,当前时间是:"+t);
TimeUnit.SECONDS.sleep(2);//停滞两秒
} catch (Exception e) {
e.printStackTrace();
} finally {
writeLock.unlock();//释放读锁
}
}
}
线程池
使用线程池的优点:
- 降低资源的消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高影响速度,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高线程的可管理性,
线程池的使用
JDK 5.0起提供了线程池相关API:ExecutorService 和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
创建四种方式:
- newCachedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("threadName;"+Thread.currentThread().getName());
}
});
}
}
- newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static void main(String[] args) {
//1.创建可固定长度的线程池
ExecutorService newExecutorService = Executors.newFixedThreadPool(3);
//创建了10个线程
for (int i = 0; i < 10; i++) {
int temp = i;
newExecutorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("threadName;"+Thread.currentThread().getName()+",i"+temp);
}
});
}
}
- newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例
public static void main(String[] args) {
//1.创建可定时线程池
ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
final int temp = i;
newScheduledThreadPool.schedule(new Runnable() {
public void run() {
System.out.println("i:" + temp);
}
}, 3, TimeUnit.SECONDS);//时间数值,时间单位
}
}
- newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行需要关闭
public static void main(String[] args) {
//1.创建单线程
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
newSingleThreadExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println("index:" + index);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
newSingleThreadExecutor.shutdown();
}