0
点赞
收藏
分享

微信扫一扫

Java并发编程学习笔记

玩物励志老乐 2022-08-09 阅读 71


前言

LZ看的是高洪岩的《Java多线程编程核心技术》和《Java并发编程核心方法与框架》,都两本书都是偏入门的书籍,《Java并发编程的艺术》和《Java并发编程实战》是业内公认的好书

Java多线程技能

实现多线程编程的方式主要有两种,继承Thread类和实现Runnable接口

线程启动顺序与start()方法执行顺序无关

共享自定义线程类中的实例变量

public class MyThread extends Thread{

private int count = 3;

@Override
public synchronized void run() {
count--;
System.out.println(this.currentThread().getName() + count);
}
}

public class Run {

public static void main(String[] args) {

MyThread myThread = new MyThread();
Thread a = new Thread(myThread, "a");
Thread b = new Thread(myThread, "b");
Thread c = new Thread(myThread, "c");
//a2
//b1
//c0

停止线程

有3种方法可以停止当前运行的线程

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止(在run方法中设置标记变量,使用while循环判断这个标志是否符合条件)
  2. 使用stop方法强行终止线程,但这个是作废的方法
  3. 使用interrupt方法中断线程
    判断线程是否是停止状态,Thread类中提供了两种方法

static boolean interrupted()
boolean isInterrupted()

interrupted() Tests whether the current thread has been interrupted,执行后具有将状态标志清除为false的功能
isInterrupted() Tests whether this thread has been interrupted,不清除状态标志

public class Run {

public static void main(String[] args) {

Thread.currentThread().interrupt();
//是否停止? true
System.out.println("是否停止? " + Thread.interrupted());
//是否停止? false
System.out.println("是否停止? "

public class MyThread extends Thread{

@Override
public synchronized void run() {
for (int i=0; i<5000; i++) {
System.out.println(i);
}
}
}

public class Run2 {

public static void main(String[] args) throws InterruptedException {

MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(20);
myThread.interrupt();
System.out.println("是否停止? " + myThread.isInterrupted());
System.out.println("是否停止? "

Java并发编程学习笔记_i++

Java并发编程学习笔记_对象锁_02


对线程调用interrupt,并不能直接停止线程的运行,会等run方法运行完毕

interrupt+异常停止线程

public class MyThread extends Thread{

@Override
public void run() {

for (int i=0; i<50000; i++) {
if (this.isInterrupted()) {
System.out.println("中断线程");
break;
}
System.out.println(i);
}
System.out.println("for循环语句后");
}
}

Java并发编程学习笔记_i++_03


能中断for循环中的,for循环之后的还会执行

public class MyThread extends Thread{

@Override
public void run() {

try {
for (int i=0; i<50000; i++) {
if (this.isInterrupted()) {
System.out.println("中断线程");
throw new InterruptedException();
}
System.out.println(i);
}
System.out.println("for循环语句后");
} catch (InterruptedException e) {
System.out.println("进入catch方法");
}
}
}

Java并发编程学习笔记_i++_04


这样就能中断线程了

先调用sleep,后调用interrupt或者先调用interrupt再调用sleep会产生异常

interrupt+return停止异常

public class MyThread extends Thread{

@Override
public void run() {

while (true) {
if (this.isInterrupted()) {
System.out.println("停止了");
return;
}
System.out.println(System.currentTimeMillis());
}
}
}

Java并发编程学习笔记_对象锁_05

yield方法

yield方法的作用是放弃当前的CPU资源,让其他任务去占用CPU执行时间。但放弃时间不确定,有可能刚放弃,马上又获得CPU时间片

public class MyThread extends Thread{

@Override
public void run() {
long beginTime = System.currentTimeMillis();
for (int i=0; i<500000; i++) {
//Thread.yield();
}
long endTime = System.currentTimeMillis();
System.out.println("运行时间"

未打开注释:运行时间3
打开注释:运行时间59

对象及变量的并发访问

synchronized同步方法

如果多个线程共同访问1个对象中的实例变量,则有可能出现“非线程安全”问题

public class SelfPrivateNum {

private int num = 0;
public void add(String userName) {
try {
if (userName.equals("a")) {
num = 100;
Thread.sleep(1000);
} else if (userName.equals("b")) {
num = 200;
}
System.out.println(userName + " " + num);
} catch

public class MyThreadA extends Thread{

private SelfPrivateNum selfPrivateNum;

public MyThreadA(SelfPrivateNum selfPrivateNum) {
this.selfPrivateNum = selfPrivateNum;
}

@Override
public void run() {
selfPrivateNum.add("a");
}
}

MyThreadB是在run方法中调用selfPrivateNum.add(“b”),其他和MyThreadA都一样

public class Run {

public static void main(String[] args) {

SelfPrivateNum selfPrivateNum = new SelfPrivateNum();
MyThreadA myThreadA = new MyThreadA(selfPrivateNum);
//b 200
myThreadA.start();
//a 200
MyThreadB myThreadB = new

调用用关键字synchronized声明的方法一定是排队运行的,但却可以异步调用非synchronized类型的方法
关键字synchronized具有锁重入的功能,当一个线程得到一个对象锁后,可以再次请求得到对象锁。因此在一个synchronized方法内部调用本类的synchronized方法时,是可以得到锁的

public class Service {

public synchronized void service1() {
System.out.println("service1");
service2();
}

public synchronized void service2() {
System.out.println("service2");
}
}

public class MyThread extends Thread{

@Override
public void run() {
Service service = new

public class Run {

public static void main(String[] args) {

MyThread myThread = new MyThread();
//service1
//service2

当存在继承关系时,子类可以通过“可重入锁”调用父类的同步方法

当出现异常时,锁被自动释放,子类从父类中继承的同步方法不具有同步性,必须自己加上synchronized

synchronized同步代码块

volatile关键字

解决异步死循环

public class RunThread extends Thread{

private boolean isRunning = true;

public boolean isRunning() {
return isRunning;
}

public void setRunning(boolean running) {
isRunning = running;
}

@Override
public void run() {
System.out.println("进入run方法");
while (isRunning) {

}
System.out.println("线程被停止");
}
}

public class Run {

public static void main(String[] args) {

try {
RunThread thread = new RunThread();
thread.start();
Thread.sleep(1000);
thread.setRunning(false);
System.out.println("已经赋值为false");
} catch

Java并发编程学习笔记_对象锁_06

线程被停止这句话始终没有被输出,是因为private boolean isRunning = true;存在于公共堆栈及线程的私有堆栈中,线程一直在私有堆栈中取得isRunning的值为true。而代码thread.setRunning(false);虽然被执行,更新的确实公共堆栈中的isRunning变量值false,所以一直就是死循环状态

Java并发编程学习笔记_ide_07

private volatile boolean isRunning = true;

将isRunning用volatile修饰,结果如图

Java并发编程学习笔记_i++_08


是因为使用volatile关键字,强制从公共内存中读取变量的值

Java并发编程学习笔记_i++_09


synchronized和volatile的对比

  1. volatile是线程同步的轻量级实现,性能比synchronized要好,volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块
  2. 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
  3. volatile能保证数据的可见性,但不能保证原子性,而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
  4. volatile解决的是变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性

volatile非原子特性

public class MyThread extends Thread{

public static volatile int count;

private static void addCount() {
for (int i=0; i<100; i++) {
count++;
}
System.out.println(count);
}

@Override
public void run() {
addCount();
}
}

public class Run {

public static void main(String[] args) {

MyThread[] myThreads = new MyThread[100];
for (int i=0; i<100; i++) {
myThreads[i] = new MyThread();
}
for (int i=0; i<100; i++) {
myThreads[i].start();
}
}
}

如果能保证原子性,则最后输出应该是10000

Java并发编程学习笔记_ide_10


使用关键字volatile时出现非线程安全的原因

变量在内存中工作的过程如下

Java并发编程学习笔记_i++_11

除了在i++操作时使用synchronized关键字实现同步外,还可以使用AtomicInteger原子类进行实现

synchronized代码块有volatile同步的功能

public class Service {

private boolean isContinueRun = true;

public void runMethod() {
while (isContinueRun) {

}
System.out.println("已经停下来了");
}
public void stopMethod() {
isContinueRun = false;
}

}

public class ThreadA extends Thread{

private Service service;

public ThreadA(Service service) {
this.service = service;
}

@Override
public void run() {
service.runMethod();
}
}

public class ThreadB extends Thread{

private Service service;

public ThreadB(Service service) {
this.service = service;
}

@Override
public void run() {
service.stopMethod();
}
}

public class Run {

public static void main(String[] args) {

try {
Service service = new Service();
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.start();
Thread.sleep(1000);
b.start();
System.out.println("已经发出停止命令");
} catch

Java并发编程学习笔记_ide_12


得到这个结果时各线程间的数据值没有可视性造成的,而关键字synchronized可以具有可视性

public class Service {

private boolean isContinueRun = true;

public void runMethod() {
while (isContinueRun) {
String anyString = new String();
synchronized (anyString) {

}
}
System.out.println("已经停下来了");
}
public void stopMethod() {
isContinueRun = false;
}

}

将Service类更改为如上后,再次运行Run类,结果为

Java并发编程学习笔记_i++_13

线程间通信

wait,notify,notifyAll都是Object类的方法,方法wait和notify要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁。在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获得该对象锁,要等到notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。

public class Test1 {

public static void main(String[] args) {

try {
String newString = new String("");
newString.wait();
} catch

Java并发编程学习笔记_i++_14


出现异常的原因是没有“对象监视器”,也就是没有同步加锁

public class MyThread1 extends Thread{

private Object lock;

public MyThread1(Object lock) {
this.lock = lock;
}

@Override
public void run() {
try {
synchronized (lock) {
System.out.println("开始 wait time = " + System.currentTimeMillis());
lock.wait();
System.out.println("结束 wait time = " + System.currentTimeMillis());
}
} catch

public class MyThread2 extends Thread{

private Object lock;

public MyThread2(Object lock) {
this.lock = lock;
}

@Override
public void run() {
synchronized (lock) {
System.out.println("开始 notify time = " + System.currentTimeMillis());
lock.notify();
System.out.println("结束 nofity time = "

public class Run {

public static void main(String[] args) {

try {
Object obj = new Object();
MyThread1 myThread1 = new MyThread1(obj);
myThread1.start();
Thread.sleep(3000);
MyThread2 myThread2 = new MyThread2(obj);
myThread2.start();
} catch

Java并发编程学习笔记_i++_15

wait被执行后,锁被自动释放,但执行完notify方法,锁却不自动释放,必须执行完notify方法所在的同步synchronized代码块后才释放锁

将MyThread2的代码更改如下,再次运行

public class MyThread2 extends Thread{

private Object lock;

public MyThread2(Object lock) {
this.lock = lock;
}

@Override
public void run() {
try {
synchronized (lock) {
System.out.println("开始 notify time = " + System.currentTimeMillis());
lock.notify();
Thread.sleep(3000);
System.out.println("结束 nofity time = " + System.currentTimeMillis());
}
} catch

Java并发编程学习笔记_ide_16


可以看到开始通知和结束通知之间隔了3秒

调用notify一次只随机通知一个线程进行唤醒,为了唤醒所有线程,可以使用notifyAll方法

生产者消费者模式实现

一生产一消费:操作值

public class P implements Runnable{

private String lock;

public P(String lock) {
this.lock = lock;
}

public void setValue() {
try {
synchronized (lock) {
if (!ValueObject.vlaue.equals("")) {
lock.wait();
}
String value = System.currentTimeMillis() + "";
System.out.println("set的值为 " + value);
ValueObject.vlaue = value;
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

@Override
public void run() {
while (true) {
setValue();
}
}
}

public class C implements Runnable{

private String lock;

public C(String lock) {
this.lock = lock;
}

public void getValue() {
try {
synchronized (lock) {
if (ValueObject.vlaue.equals("")) {
lock.wait();
}
System.out.println("get的值为 " + ValueObject.vlaue);
ValueObject.vlaue = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

@Override
public void run() {
while (true) {
getValue();
}
}
}

public class ValueObject {
public static String vlaue = "";
}

public class Run {

public static void main(String[] args) {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
Thread threadp = new Thread(p);
Thread threadc = new

多生产多消费:操作值-假死
将上面代码中的P类和C类的if语句变为while语句

while (!ValueObject.vlaue.equals("")) {
lock.wait();
System.out.println(Thread.currentThread().getName() + " waiting");

Run更改如下

public class Run {

public static void main(String[] args) throws InterruptedException {
String lock = new String("");
P p = new P(lock);
C c = new C(lock);
Thread[] threadp = new Thread[2];
Thread[] threadc = new Thread[2];
for (int i=0; i<2; i++) {
threadp[i] = new Thread(p, "生产者" + (i + 1));
threadp[i].start();
threadc[i] = new Thread(c, "消费者" + (i + 1));
threadc[i].start();
}
Thread.sleep(5000);
Thread[] threads = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threads);
for (int i = 0; i < threads.length; i++) {
System.out.println("状态" + threads[i].getName() + " "

Java并发编程学习笔记_ide_17


有可能出现假死的情况,原因是有可能出现“生产者”唤醒“生产者”,或者“消费者”唤醒“消费者”的情况,解决的办法就是将P类和C类的notify改成notifyAll方法

方法join的使用

public class MyThread extends Thread{

@Override
public void run() {
try {
Thread.sleep(3000);
System.out.println("子线程运行结束");
} catch

public class Run {

public static void main(String[] args) {

MyThread myThread = new MyThread();
myThread.start();
//Thread.sleep();有时候我不知道子线程运行多长时间,所以不确定值
System.out.println("主线程运行完毕");
}
}

Java并发编程学习笔记_ide_18


将Run类更改为如下

public class Run {

public static void main(String[] args) {

try {
MyThread myThread = new MyThread();
myThread.start();
myThread.join();
//Thread.sleep();有时候我不知道子线程运行多长时间,所以不确定值
System.out.println("主线程运行完毕");
} catch

Java并发编程学习笔记_对象锁_19

在join过程中,如果当前线程对象被中断,则当前线程出现异常
join(long)中的参数时设定等待时间,超时就不再等待,方法join(long)的功能是在内部使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点,而Thread.sleep(long)方法却不释放锁

ThreadLocal的使用

public class Tools {
public static ThreadLocal t1 = new

public class ThreadA extends Thread{

@Override
public void run() {
try {
for (int i=0; i<10; i++) {
Tools.t1.set("ThreadA " + i);
Thread.sleep(200);
System.out.println("ThreadA get value = " + Tools.t1.get());
}
} catch

public class Run {

public static void main(String[] args) {

try {
ThreadA threadA = new ThreadA();
threadA.start();
for (int i=10; i<20; i++) {
Tools.t1.set("Main " + i);
Thread.sleep(200);
System.out.println("Main get value = " + Tools.t1.get());
}
} catch

Java并发编程学习笔记_i++_20

Lock的使用

public class MyService {

private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
for (int i=0; i<5; i++) {
System.out.println("Thread Name " + Thread.currentThread().getName() + " " + i);
}
lock.unlock();
}
}

public class MyThread extends Thread{

MyService myService;

public MyThread(MyService myService) {
this.myService = myService;
}

@Override
public void run() {
myService.testMethod();
}
}

public class Run {

public static void main(String[] args) {
MyService myService = new MyService();
MyThread myThread = new MyThread(myService);
MyThread myThread1 = new

Java并发编程学习笔记_对象锁_21


顺序打印

调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放时再次争抢

使用Condition实现等待通知

public class MyService {

private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await() {
try {
//condition.await()方法调用之前调用lock.lock()代码获得同步监视器
lock.lock();
System.out.println("await时间为 " + System.currentTimeMillis());
condition.await();
} catch (InterruptedException e){
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() {
try {
lock.lock();
System.out.println("signal时间为 " + System.currentTimeMillis());
condition.signal();
} finally {
lock.unlock();
}

}
}

public class ThreadA extends Thread{

private MyService myService;

public ThreadA(MyService myService) {
this.myService = myService;
}

@Override
public void run() {
myService.await();
}
}

public class Run {

public static void main(String[] args) throws InterruptedException {
MyService myService = new MyService();
ThreadA threadA = new ThreadA(myService);
threadA.start();
Thread.sleep(3000);
myService.signal();
}
}

Java并发编程学习笔记_对象锁_22

Object

Condition

wait

await

wait(long timeout)

await(long time,TimeUnit unit)

notify

signal

notify

signalAll

使用多个Condition实现通知部分线程

public class MyService {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();

public void awaitA() {
try {
lock.lock();
System.out.println("begin awaitA 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
conditionA.await();
System.out.println("end awaitA 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void awaitB() {
try {
lock.lock();
System.out.println("begin awaitB 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
conditionB.await();
System.out.println("end awaitB 时间为" + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}

public void signalAll_A() {
try {
lock.lock();
System.out.println("signalAll_A时间为 " + System.currentTimeMillis() + " ThreadName = " + Thread.currentThread().getName());
conditionA.signalAll();
} finally {
lock.unlock();
}
}

}

public class ThreadA extends Thread{

private MyService myService;

public ThreadA(MyService myService) {
this.myService = myService;
}

@Override
public void run() {
myService.awaitA();
}
}

public class Run {

public static void main(String[] args) {

try {
MyService myService = new MyService();
ThreadA threadA = new ThreadA(myService);
threadA.setName("A");
threadA.start();
ThreadB threadB = new ThreadB(myService);
threadB.setName("B");
threadB.start();
Thread.sleep(3000);
myService.signalAll_A();
} catch

ThreadB线程调用MyService的awaitB方法

Java并发编程学习笔记_ide_23


因为B线程没有结束,所以始终没有停止

Future和Callable的使用

Callable接口和Runnable接口的区别

  1. Callable接口的call()方法可以有返回值,而Runnable接口的run()方法没有返回值
  2. Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常

线程池execute()和submit()的区别

  1. execute()方法没有返回值,submit()方法可以有返回值
  2. 2.

举报

相关推荐

0 条评论