一、线程间的共享
synchronized内置锁
对象锁,锁的是类的对象实例。
类锁 ,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。
class Sync {
public void test() {
synchronized (Sync.class) {
System.out.println("test开始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test结束..");
}
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
volatile关键字
适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。
package com.enjoy.demo.p1.ch1.class1;
/**
* @Author: BillYu
* volatile应用场景:一个线程写多个线程读
* @Description:演示volatile无法提供操作的原子性
* @Date: Created in 15:44 2019-02-20.
*/
public class VolatileUnsafe {
private static class VolatileVar implements Runnable{
private volatile int a = 0;
@Override
public void run() {
String threadName = Thread.currentThread().getName();
a = a+1;
System.out.println(threadName+":===="+a);
SleepTools.ms(100);
a = a +1;
System.out.println(threadName+":===="+a);
}
}
public static void main(String[] args) {
VolatileVar v = new VolatileVar();
Thread t1 = new Thread(v);
Thread t2 = new Thread(v);
Thread t3 = new Thread(v);
Thread t4 = new Thread(v);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
ThreadLocal
线程变量。可以理解为是个map,类型 Map<Thread,Integer>
package com.enjoy.demo.p1.ch1.class1;
/**
* @Author: BillYu
* @Description:ThreadLocal使用
* 每个线程拥有自己线程变量副本,保证线程之间变量不会冲突
* 注意:尽量存储一些占用内存比较小的变量,否则会占用太多内存
* @Date: Created in 15:54 2019-02-20.
*/
public class UseThreadLocal {
static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
/**
* 运行3个线程
*/
public void startThreadArray(){
Thread [] runs = new Thread[3];
for (int i =0;i<runs.length;i++){
runs[i] = new Thread(new TestThread(i));
}
for (int i = 0;i<runs.length;i++){
runs[i].start();
}
}
/**
* 类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会相互影响
*/
public static class TestThread implements Runnable{
int id;
public TestThread(int id){
this.id = id;
}
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+":start");
//获得变量的值
Integer s = threadLocal.get();
s = s + id;
threadLocal.set(s);
System.out.println(Thread.currentThread().getName()+ threadLocal.get());
}
}
public static void main(String[] args) {
UseThreadLocal test = new UseThreadLocal();
test.startThreadArray();
}
}
线程间协作
轮询:难以保证及时性,资源开销很大,
等待和通知
wait() 对象上的方法
notify/notifyAll 对象上的方法
package com.enjoy.demo.p1.ch1.class2;
/**
* @Author: BillYu
* @Description: wait notify notifyAll
* @Date: Created in 11:19 2019-02-25.
*/
public class WaitNotify {
private Object lock = new Object();
public class WaitThread extends Thread{
@Override
public void run() {
try {
synchronized (lock){
System.out.println("begin wait() ThreadName = "+Thread.currentThread().getName());
lock.wait();
//当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
System.out.println("end wait() ThreadName = "+Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class NotifyThread extends Thread{
@Override
public void run() {
try {
synchronized (lock){
System.out.println("begin notify() threadName = "
+ Thread.currentThread().getName()+" time = "
+ System.currentTimeMillis());
// lock.notify();
lock.notifyAll();
// 当执行notify/notifyAll方法时,会唤醒一个处于等待该 对象锁 的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁。
Thread.sleep(5000);
System.out.println(" end notify() ThreadName = "+ Thread.currentThread().getName()+" time ="
+System.currentTimeMillis());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
WaitNotify test = new WaitNotify();
// test.new WaitThread().start();
WaitThread waitThread1 = test.new WaitThread();
waitThread1.start();
WaitThread waitThread2 = test.new WaitThread();
waitThread2.start();
WaitThread waitThread3 = test.new WaitThread();
waitThread3.start();
test.new NotifyThread().start();
/**
* 实验测试结果:
* notify唤醒顺序:唤醒最早执行wait()的线程
* 处于wait状态的线程会被虚拟机放在一个队列中,第一个放进去的线程会被notify先唤醒
*
* notifyAll唤醒顺序:最晚执行wait()的线程最早被唤醒
* 应该尽量使用notifyAll, 因为使用notify有可能发生信号丢失的情况,被同样等待通知的线程拦截
*/
}
}
等待和通知的标准范式
等待方:
1、 获取对象的锁;
2、 循环里判断条件是否满足,不满足调用wait方法,
3、 条件满足执行业务逻辑
通知方来说
1、 获取对象的锁;
2、 改变条件
3、 通知所有等待在对象的线程
notify和notifyAll应该用谁?
应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况
join()方法
线程A,执行了线程B的join方法,线程A必须要等待B执行完成了以后,线程A才能继续自己的工作
package com.enjoy.demo.p1.ch1.class2;
import com.enjoy.demo.p1.ch1.class1.SleepTools;
/**
* @Author: BillYu
* @Description:演示join方法的使用
* @Date: Created in 15:42 2019-02-21.
*/
public class UseJoin {
static class JumpQueue implements Runnable{
/**
* 用来插队的线程
*/
private Thread thread;
public JumpQueue(Thread thread){
this.thread = thread;
}
@Override
public void run(){
try{
thread.join();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" terminated.");
}
}
public static void main(String[] args) {
//拿到主线程
Thread previous = Thread.currentThread();
for (int i =0 ;i<10;i++) {
Thread thread = new Thread(
new JumpQueue(previous),String.valueOf(i)
);
System.out.println(previous.getName()+" jump a queue the thread:"+ thread.getName());
thread.start();
previous = thread;
}
//让祝线程休眠2秒
SleepTools.second(2);
System.out.println(Thread.currentThread().getName()+" terminate");
}
}
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
线程在执行yield()以后,持有的锁是不释放的
sleep()方法被调用以后,持有的锁是不释放的
调动方法之前,必须要持有锁。调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁
调动方法之前,必须要持有锁,调用notify()方法本身不会释放锁的
package com.enjoy.demo.p1.ch1.class2;
/**
* @Author: BillYu
* @Description: 验证线程休眠不释放锁
* @Date: Created in 10:26 2019-02-25.
*/
public class SleepLock {
private Object lock = new Object();
public static void main(String[] args) {
SleepLock sleepTest = new SleepLock();
Thread threadA = sleepTest.new ThreadSleep();
threadA.setName("ThreadSleep");
Thread threadB = sleepTest.new ThreadNotSleep();
threadB.setName("ThreadNotSleep");
threadA.start();
try {
Thread.sleep(1000);
System.out.println("Main slept");
} catch (InterruptedException e) {
e.printStackTrace();
}
threadB.start();
}
/**
* 休眠线程
*/
private class ThreadSleep extends Thread{
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("threadName"+ "will take the lock");
//alt shift z
try {
synchronized (lock){
System.out.println(threadName+" taking the lock");
Thread.sleep(5000);
System.out.println("Finish the work : "+threadName);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 不休眠线程
*/
private class ThreadNotSleep extends Thread{
@Override
public void run(){
String threadName = Thread.currentThread().getName();
System.out.println(threadName+ " will take the lock time = "+System.currentTimeMillis());
synchronized (lock){
System.out.println(threadName+" taking the lock time = "+System.currentTimeMillis());
System.out.println("Finish the work: "+threadName);
}
}
}
}
wait、notify对应的生产者、消费者问题
模拟场景:产品数量大于0时停止生产,通知消费者消费,产品数量为0时停止消费,通知生产者生产
package com.enjoy.demo.p1.ch1.class2;
/**
* @Author: BillYu
* @Description:生产者消费者
* @Date: Created in 13:33 2019-02-25.
*/
public class ProduceConsume {
private Object lock = new Object();
private Integer productNum = 0;
public class ProduceThread extends Thread {
@Override
public void run() {
while (true) {
synchronized (lock) {
while (productNum > 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
productNum = productNum + 5;
System.out.println("生产5个产品,当前产品数量: " + productNum);
lock.notifyAll();
}
}
}
}
public class ConsumeThread extends Thread {
@Override
public void run() {
while (true) {
synchronized (lock) {
while (productNum == 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
productNum = productNum - 1;
System.out.println(Thread.currentThread()+"消费1个产品,当前产品数量:" + productNum);
lock.notifyAll();
}
}
}
}
public static void main(String[] args) {
ProduceConsume main = new ProduceConsume();
ConsumeThread consume1 = main.new ConsumeThread();
ConsumeThread consume2 = main.new ConsumeThread();
consume1.start();
consume2.start();
ProduceThread produce1 = main.new ProduceThread();
produce1.start();
}
}