线程同步-可见性(非线程安全)
(1)在并发线程中存在的线程安全问题,主要原因有
[1]存在共享数据
[2]多线程共同操作共享数据
(2)volatile关键词---可见性(Visibility)
[1] 功能:volatile关键字的作用很简单,就是一个线程在对主内存的某一份数据进行更改时,更改之后会立刻刷新主内存,并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据,这样一来就保证了可见性
[2] 案例分析(注意volative关键字的作用,可以尝试将该关键词删除后进行运行)
package com.experiment_2_Thread.four;
public class TestVo extends Thread{
//stop相当于是一个环境变量
//我们加入了一个volatile关键词,实现了刷新主内存的作用
public volatile static boolean stop=false;
@Override
public void run() {
System.out.println("我开始了");
while (!stop) {
}
System.out.println("我结束了");
}
//我们将sleep可能产生的异常进行抛出
public static void main(String[] args) throws InterruptedException {
TestVo testVo=new TestVo();
testVo.start();
//当前线程睡眠2s
Thread.sleep(2000);
stop=true;
}
}
线程同步-原子性
(1)介绍:
[1]进入区(申请资源)------ 资源被占用,暂时不能访问,进入阻塞等待队列
[2]临界区 -----只能有一个线程
[3]退出去(释放资源)
(2)Synchronized关键字
[1]概述:java中每一个对象都可以有锁,这是Synchronized实现同步的基础
[2]三种应用方式:
{1}普通同步方法(实例方法),锁是当前实例对象,进入同步代码前要获得当前实例的锁
{2}静态同步方法,锁是当前类的class对象,进入同步代码前要获得当前类对象的锁
{3}同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
[3]案例分析
{普通同步方法}
{T1线程}
package com.experiment_2_Thread.four;
public class T1 implements Runnable {
synchronized public void f1() {
System.out.println(Thread.currentThread().getName()+"开始了");
try {
//让线程进行休眠2s
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束了");
}
@Override
public void run() {
// TODO Auto-generated method stub
f1();
}
}
{Testsy}
package com.Test_P;
public class Testsy {
public static void main(String[] args) {
T1 t=new T1();
Thread t1 = new Thread(t,"测试1");
Thread t2 = new Thread(t,"测试2");
t1.start();
t2.start();
}
}
运行结果:线程1和线程2依次进行运行,线程1运行结束后才开始运行线程2
{静态同步方法}
{T1线程}
package com.Test_P;
public class T1 implements Runnable {
synchronized static public void f1() {
System.out.println(Thread.currentThread().getName()+"开始了");
try {
//让线程进行休眠2s
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束了");
}
@Override
public void run() {
// TODO Auto-generated method stub
f1();
}
}
{Testsy}
package com.Test_P;
public class Testsy {
public static void main(String[] args) {
T1 t=new T1();
T1 tt=new T1();
Thread t1 = new Thread(t,"测试1");
Thread t2 = new Thread(t,"测试2");
Thread t3 = new Thread(tt,"测试3");
t1.start();
t2.start();
t3.start();
}
}
静态同步方法才让线程1,线程2,线程3依次执行,否则由于t3和t1,t2的对象不同会导致交错运行的结果
这个静态方法锁相当于是给类上了一个锁,只要是调用这个类的就必须要有锁
{同步方法块}
{不需要同步的System.out.println("测试")我们就没有放到同步语句块中}
{最上面的那个Object object = new Object(); 如果没有static就相当于普通同步方法}
package com.experiment_2_Thread.four;
public class T1 implements Runnable {
//Object object=new Object();
static Object object=new Object();
public void f1() {
System.out.println("测试");
synchronized(object)
{
System.out.println(Thread.currentThread().getName()+"开始了");
try {
//让线程进行休眠2s
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"结束了");
}
}
@Override
public void run() {
// TODO Auto-generated method stub
f1();
}
}
{Testsy}
package com.experiment_2_Thread.four;
public class Testsy {
public static void main(String[] args) {
T1 t=new T1();
T1 tt=new T1();
Thread t1=new Thread(t,"测试1");
Thread t2=new Thread(t,"测试2");
Thread t3=new Thread(tt,"测试3");
t1.start();
t2.start();
t3.start();
}
}
(3)sychronized的缺点
[1]效率低
[2]锁的释放情况少,只在程序正常执行完成和抛出异常时释放锁
[3]试图获得锁是不能设置超时的
[4]不能中断一个正在试图获得锁的线程
[5]无法知道是否成功获得到锁
Lock接口与ReentrantLock接口
(1)与synchronized关键字相同的地方:他们实现的功能是一样的
(2)与synchronized关键字不同的地方:
[1]Lock有比synchronized更精确的线程语义和更好的性能
[2]synchronized会自动释放锁,Lock要求程序员手工释放,且必须在finally语句中
(3)使用锁进行同步机制(实例)
{T2线程}
package com.experiment_2_Thread.four;
import java.util.concurrent.locks.ReentrantLock;
public class T2 implements Runnable {
ReentrantLock r=new ReentrantLock();
public void f1() {
r.lock();
try {
System.out.println(Thread.currentThread().getName() + "开始了");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束了");
}
//因为还锁只能在finally中进行所以必须要加try语句块
finally {
r.unlock();
}
}
@Override
public void run() {
// TODO Auto-generated method stub
f1();
}
}
{TestLock}
package com.experiment_2_Thread.four;
public class TestLock {
public static void main(String[] args) {
T2 t2=new T2();
Thread thread1=new Thread(t2,"测试1");
thread1.start();
Thread thread2=new Thread(t2,"测试2");
thread2.start();
}
}
wait()和notify()
(1)wait():导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法
(2)notify():唤醒正在等待的对象监视器的单个线程
(3)notifyAll():唤醒正在等待的对象监视器的所有线程
(4)实例分析
package com.experiment_2_Thread.four;
public class TestWait {
static Object object = new Object();
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
public void run(){
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "开始了");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束了");
}
}
});
Thread thread1 = new Thread(new Runnable() {
public void run(){
synchronized (object) {
System.out.println(Thread.currentThread().getName() + "开始了");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束了");
object.notify();
}
}
});
thread.start();
thread1.start();
}
}