synchronized和volatile
volatile :保证内存可见性,但是不保证原子性;
synchronized:同步锁,既能保证内存可见性,又能保证原子性;
synchronized实现可重入锁 (1.持有同一锁自动获取 2.继承锁)
锁定的对象有两种:1.类的实例(对象锁) 2.类对象(类锁)
对象锁(synchronized修饰普通方法或代码块) 对象锁已被其他调用者占用,则需要等待此锁被释放
/**
* 对象锁的两种方式
*/
//方式一
private int count =10;
public synchronized void test01() {
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
} //方式二
public void test02() {
synchronized(this) {
count--;
System.out.println(Thread.currentThread().getName()+"count="+count);
}
}
类锁(synchronized修饰静态方法) 所有类实例化对象互斥拥有一把类锁
private static int count =10;
/**
* 类锁两种表现方式
*/
public static synchronized void test01() {
count -- ;
System.out.println(Thread.currentThread().getName()+"count="+count);
} public static void test02() {
synchronized(CurrentDemo01.class) {
count -- ;
System.out.println(Thread.currentThread().getName()+"count="+count);
}
}
同一字符串常量代表同一把锁对象
//t1执行结束,t2再执行,t1和t2持有同一把锁
public class CurrentDemo03 { String s1 = "yew";
String s2 = "yew"; public void test01(){
synchronized (s1){
System.out.println(Thread.currentThread().getName()+"---start");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
System.out.println("interrupt");
}
System.out.println(Thread.currentThread().getName()+"---end");
}
} public void test02(){
synchronized (s2){
System.out.println(Thread.currentThread().getName()+"---start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("interrupt");
}
System.out.println(Thread.currentThread().getName()+"---end");
}
} public static void main(String[] args) {
CurrentDemo03 demo3 = new CurrentDemo03();
new Thread(demo3::test01,"t1").start();
new Thread(demo3::test02,"t2").start();
}
}
synchronized同步代码块粒度越小,执行效率越高
public class CurrentDemo04 { //总数
private static int count = 0;
public synchronized void test01() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"---start");
TimeUnit.SECONDS.sleep(2);
for (int i = 0; i <10000000 ; i++) {
count++ ;
}
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"---end");
} public void test02() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"---start");
TimeUnit.SECONDS.sleep(2);
synchronized (this){
for (int i = 0; i <10000000 ; i++) {
count++ ;
}
}
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"---end");
} //demo04.test01();8043ms
// demo04.test02();4116ms
public static void main(String[] args){
CurrentDemo04 demo04 = new CurrentDemo04();
long start = System.currentTimeMillis();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo04.test02();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
demo04.test02();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
System.out.println(Thread.activeCount());
while(Thread.activeCount()>2){
Thread.yield();
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start)+"ms");
System.out.println(count);
}
}
synchronized方法正常返回或者抛异常而终止,jvm会自动释放对象锁(捕获异常则不会自动释放)
public class CurrentDemo08 { //总数值
private int count = 0; public synchronized void test01() throws Exception {
System.out.println(Thread.currentThread().getName()+"---start"); while (true){
count++ ;
TimeUnit.SECONDS.sleep(1);
if(count <5){
System.out.println(count);
}else{
try {
int m = count/0;
}catch (Exception e){
System.out.println("ERROR :"+e.getMessage());
// return;
}
}
}
} public synchronized void test02() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+"----start"); TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"---end");
} public static void main(String[] args) { CurrentDemo08 demo08 = new CurrentDemo08(); new Thread(()-> {
try {
demo08.test01();
} catch (Exception e) {
System.out.println("test01中断");
}
},"t1").start(); new Thread(()-> {
try {
demo08.test02();
} catch (InterruptedException e) {
System.out.println("test02中断");
}
},"t2").start();
}
}
/**
* @author yew
* @date on 2019/11/18 - 11:49
* 一道面试题:实现一个容器,提供两个方法,add,size
* 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,
* 当个数到5个时,线程2给出提示并结束线程2
* 1.无volatile,线程间不可见,线程t2不会结束
* 2.volatile可以保证原子的可见性,存在不确定性 while(true) 占用CPU资源
*/
public class CurrentDemo11 {
//List<Object> lists = new ArrayList<Object>();
volatile List<Object> lists = new ArrayList<Object>(); public void add(Object s) {
lists.add(s);
}
public int size() {
return lists.size();
} public static void main(String[] args) {
CurrentDemo11 demo11 = new CurrentDemo11(); new Thread(() -> {
for (int i = 0; i < 10; i++) {
demo11.add(new Object());
System.out.println("add Object" + (i + 1));
try {
TimeUnit.MILLISECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t1线程结束");
}, "t1").start(); new Thread(() -> {
System.out.println("t2线程开始");
while (true) {
if (demo11.size() == 5){
break;
}
}
System.out.println("t2线程结束");
}, "t2").start();
}
} 优化上述问题:
/**
* @author yew
* 1.wait----notify(随机唤醒持有当前锁且等待的某个线程)/notifyAll(唤醒持有当前锁所有的等待线程)
* notify随机唤醒持有锁等待的线程,但是不会释放当前持有的锁 所以监控线程不会立马结束
* 2.countdownLatch
*/
public class CurrentDemo12 { List lists = new ArrayList(); public int size() {
return this.lists.size();
} public void add(Object obj) {
this.lists.add(obj);
} public static void main(String[] args) {
CurrentDemo12 demo12 = new CurrentDemo12(); Object lock = new Object(); //先启用t2进行监听
new Thread(()->{
synchronized (lock){
System.out.println("t2线程启动");
if(demo12.size() != 5){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2线程结束");
lock.notify();
} },"t2").start(); new Thread(()->{
synchronized (lock){
System.out.println("t1线程启动");
for (int i = 0; i < 10 ; i++) {
System.out.println("add Object"+(i+1));
demo12.add(new Object());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(demo12.size() == 5){
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("t1线程结束");
}
}).start();
}
}
/** * 面试题:写一个固定容量同步容器,拥有Put和get方法,以及getCount方法,
* 能够支持两个生产者线程以及10个消费者线程的阻塞调用
* wait notifyAll
*/
public class CurrentDemo13 {
private static final int MAX = 20;
private final LinkedList<Object> list = new LinkedList<>();
static int count; public synchronized void put(Object obj){
while (list.size() == MAX){
try {
this.wait();
} catch (InterruptedException e) {
System.out.println();
}
}
list.add(obj);
++count;
System.out.println("生产后剩余数量"+count);
this.notifyAll();
} public synchronized Object get(){
while (list.size()==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object t = list.removeFirst();
--count;
System.out.println("消费后剩余数量"+count);
this.notifyAll();
return t;
} public int getCount(){
return count;
} public static void main(String[] args) {
CurrentDemo13 demo13 = new CurrentDemo13();
//创建生产者
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true){
demo13.put(Thread.currentThread().getName()+"---"+count);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
} try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
while (true){
System.out.println(demo13.get());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
}
/**
* condition:在某种状态下
*/
public class CurrentDemo14 {
LinkedList<Object> lists = new LinkedList<>();
private static final int MAX = 20;
private static int count = 0;
ReentrantLock lock = new ReentrantLock();
Condition producer = lock.newCondition();
Condition consumer = lock.newCondition(); public void put(Object obj){ try {
lock.lock();
while (lists.size()==MAX){
producer.await();
}
lists.add(obj+"----"+count);
count++;
System.out.println("生产后剩余:"+count);
Thread.sleep(500);
consumer.signalAll();
} catch (Exception e) {
System.out.println("生产线程异常中断"+e.getMessage());
}finally {
lock.unlock();
}
} public Object get(){
Object t = null;
try {
lock.lock();
while (lists.size()==0){
consumer.await();
}
t = lists.removeFirst();
System.out.println("已消费对象:"+t);
count--;
System.out.println("消费后剩余:"+count);
Thread.sleep(2000);
producer.signalAll();
} catch (Exception e) {
System.out.println("消费线程异常中断"+e.getMessage());
}finally {
lock.unlock();
return t;
}
} public static void main(String[] args){
CurrentDemo14 currentDemo14 = new CurrentDemo14();
for (int i = 0; i < 2; i++) {
new Thread("p"+i){
public void run(){
for (;;) {
currentDemo14.put(Thread.currentThread().getName());
}
}
}.start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
new Thread("c"+i){
public void run(){
for (;;) {
currentDemo14.get();
}
}
}.start();
}
}
}
/**
* ReentrantLock
* ReentrantLock同synchronized效果相同
* 1.synchronized遇到异常,自动释放锁,reentranLock需要手动释放锁,所以释放锁需要放在finally代码块执行;
* 2.tryLock() 尝试拿锁,拿不到锁的时候可以根据返回的boolean值来决定是否继续执行
* 3.lockInterruptibly()可以对线程中断做出反应
*/
public class CurrentDemo15 { ReentrantLock lock = new ReentrantLock(); public void test01(){
try{
lock.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"test 01");
int t = 10/0;
}catch (Exception e){
System.out.println("ERROR");
}finally {
lock.unlock();
}
} public void test02(){
try{
// lock.tryLock(3, TimeUnit.SECONDS);
// lock.lockInterruptibly();
lock.lock();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"test 02");
}catch (Exception e){
System.out.println("ERROR");
}finally {
lock.unlock();
}
} public static void main(String[] args) {
CurrentDemo15 currentDemo15 = new CurrentDemo15();
new Thread("t1"){
public void run(){
currentDemo15.test01();
}
}.start(); try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} Thread t2 = new Thread("t2"){
public void run(){
currentDemo15.test02();
}
};
t2.start(); try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt();
}
}