知识点总结
- 静态变量:在java中,静态变量指的是被static修饰的类的变量;静态变量被所有类实例对象所共享,在内存中只有一个副本,当且仅当在类初次加载时会被初始化。
- synchronized关键字需要对同一个实例加锁才能实现同步的作用,因此在初版代码中采用先生成一个SynchronizedCounter实例,再通过MySynchronizedAddThread、MySynchronizedDelThread构造函数初始化,使两个线程共享同一个实例
- 优化的方法:静态变量是所有实例对象共享的变量,利用静态变量的特性,可以省去初始化两个进程的步骤,代码更加简洁。
- 使用java.util.concurrent包中的ReentrantLock可以替代synchronized加锁
一、自己写的代码
package org.meituan.javalearn.thread;
/**
* @projectName: codebetter
* @package: org.meituan.javalearn.thread
* @className: SynchronizedThread
* @author: fangjiayueyuan
* @description: 线程同步
* @date: 2023/5/3 上午11:13
* @version: 1.0
*/
public class SynchronizedThread {
public static void main(String[] args) throws InterruptedException {
SynchronizedCounter synchronizedCounter = new SynchronizedCounter();
Thread adder = new MySynchronizedAddThread(synchronizedCounter);
Thread del = new MySynchronizedDelThread(synchronizedCounter);
adder.start();
del.start();
adder.join();
del.join();
System.out.println(synchronizedCounter.count);
}
}
class SynchronizedCounter{
public int count=0;
public Object locker = new Object();
}
class MySynchronizedAddThread extends Thread{
SynchronizedCounter synchronizedCounter;
public MySynchronizedAddThread(SynchronizedCounter synchronizedCounter){
this.synchronizedCounter = synchronizedCounter;
}
@Override
public void run() {
synchronized (synchronizedCounter.locker){
for (int i = 0; i < 100 ; i++) {
synchronizedCounter.count+=1;
}
}
}
}
class MySynchronizedDelThread extends Thread{
SynchronizedCounter synchronizedCounter;
public MySynchronizedDelThread(SynchronizedCounter synchronizedCounter){
this.synchronizedCounter=synchronizedCounter;
}
@Override
public void run() {
synchronized (synchronizedCounter.locker){
for (int i = 0; i < 100; i++) {
synchronizedCounter.count-=1;
}
}
}
}
二、修改后的代码
package org.meituan.javalearn.thread;
/**
* @projectName: codebetter
* @package: org.meituan.javalearn.thread
* @className: SynchronizedThread
* @author: fangjiayueyuan
* @description: 线程同步
* @date: 2023/5/3 上午11:13
* @version: 1.0
*/
public class SynchronizedThread {
public static void main(String[] args) throws InterruptedException {
Thread adder = new MySynchronizedAddThread();
Thread del = new MySynchronizedDelThread();
adder.start();
del.start();
adder.join();
del.join();
System.out.println(SynchronizedCounter.count);
}
}
class SynchronizedCounter{
public static int count=0;
public static Object lock = new Object();
}
class MySynchronizedAddThread extends Thread{
@Override
public void run() {
synchronized (SynchronizedCounter.lock){
for (int i = 0; i < 100 ; i++) {
SynchronizedCounter.count+=1;
}
}
}
}
class MySynchronizedDelThread extends Thread{
@Override
public void run() {
synchronized (SynchronizedCounter.lock){
for (int i = 0; i < 100; i++) {
SynchronizedCounter.count-=1;
}
}
}
}
三、构造线程安全的类
线程安全的类:可以支持多线程并发的类
下面的代码 c1在声明时需要通过final声明,通过final声明引用需要注意:
final修饰的引用一旦指向一个对象,就不能在重新指向其他对象,虽然指向不能改变,但是该引用指向的对象内部的数据是可以修改的。
package org.meituan.javalearn.thread;
/**
* @projectName: codebetter
* @package: org.meituan.javalearn.thread
* @className: SafeThread
* @author: fangjiayueyuan
* @description: 构造线程安全的类
* @date: 2023/5/3 下午12:28
* @version: 1.0
*/
public class SafeThread {
public static void main(String[] args) throws InterruptedException {
final Counter c1=new Counter();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
c1.add(i);
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
c1.del(i);
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(c1.getCount());
}
}
class Counter{
private static int count=0;
public void add(int n){
synchronized (this){
count+=n;
}
}
public void del(int n){
synchronized (this){
count-=n;
}
}
public int getCount() {
return count;
}
}
四、ReentrantLock锁
注意:下面两种方式都可以用来获取锁,且java中的锁是可重用锁,可以对一个线程反复上锁,上锁和释放锁要成对出现
方法1:
lock.lock();
方法2:
if(lock.tryLock(1, TimeUnit.SECONDS)) // 如果1秒后仍未获取到锁,tryLock()返回false,程序就可以做一些额外处理,而不是无限等待下去。
示例程序:最初的代码使用了两种方式上锁(下面代码中注释掉的部分),由于先运行的线程1,导致线程2在运行lock.lock();
一直抢不到锁,JVM进程也无法退出。
package org.meituan.javalearn.thread;
import lombok.SneakyThrows;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* @projectName: codebetter
* @package: org.meituan.javalearn.thread
* @className: ReentranceLocker
* @author: fangjiayueyuan
* @description: 可重入锁
* @date: 2023/5/4 下午12:05
* @version: 1.0
*/
public class ReentranceLocker {
public static void main(String[] args) throws InterruptedException {
final CounterReentranceLocker counter = new CounterReentranceLocker();
Thread thread1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
for (int i = 0; i < 2; i++) {
counter.add(i);
}
}
});
Thread thread2 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
for (int i = 0; i < 2; i++) {
counter.del(i);
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(counter.getCount());
}
}
class CounterReentranceLocker{
private int count;
private final ReentrantLock lock = new ReentrantLock();
public void add(int n) throws InterruptedException {
// lock.lock();
if(lock.tryLock(1, TimeUnit.SECONDS)){
try{
count+=n;
}finally {
lock.unlock();
}
}
// lock.unlock();
}
public void del(int n) throws InterruptedException {
// lock.lock();
if(lock.tryLock(1, TimeUnit.SECONDS)){
try{
count-=n;
}finally {
lock.unlock();
}
}
// lock.unlock();
}
public int getCount() {
return count;
}
}