不同种类的线程之间的通信问题
线程的随机性导致数据安全性问题
void wait()
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 wait()方法一但等待,就必须释放锁。唤醒之后多次线程还会再次争抢时间片。从哪里等待,被唤醒后还会在此处醒来。
void wait(long timeout)
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
void wait(long timeout, int nanos)
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
void notify()
唤醒在此对象监视器上等待的单个线程。
void notifyAll()
唤醒在此对象监视器上等待的所有线程。
sleep方法和wait方法的区别:
共同点:可以使线程都处于阻塞状态
不同点:
代码展示:
public class MyTest{
public static void main(String[] args) {
//线程的之间的等待唤醒机制,也就是生长者消费者模式
//1.定义一个资源
//2.要有一个生产线程
//3.要有一个消费线程
//4.测试类
Student student = new Student();
SetThread th1 = new SetThread(student);
GetThread th2 = new GetThread(student);
th1.start();
th2.start();
}
}
public class Student {
public String name;
public int age;
public boolean flag = false;
//这个标记的意思是,代表有没有资源
// false代表没有资源,true 代表有资源
}
public class SetThread extends Thread {
Student student;
int i = 0;
public SetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
synchronized (student) {
//作为生产者来说:有了资源,等着,通知消费线程来消费
if (student.flag) {
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i % 2 == 0) {
student.name = "张三";
student.age = 23;
//等待...
} else {
student.name = "李四";
student.age = 24;
}
//通知消费线程,去消费
student.flag = true; //修改标记
student.notify();//唤醒等待的线程,唤醒之后,多个线程还要再次争抢时间片
i++;
}
}
}
}
public class GetThread extends Thread {
Student student;
public GetThread(Student student) {
this.student = student;
}
@Override
public void run() {
while (true) {
synchronized (student) {
if (!student.flag) {
try {
student.wait();
//没有资源等待,wait()方法一旦等待,
// 就必须释放锁,从哪里等待,被唤醒后,就从这里醒来
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费了资源,通知生产线程去生产
System.out.println(student.name + "===" + student.age);
student.flag = false;
student.notify();
//唤醒等待的 线程
}
}
}
}
1.Java内存模型
想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的。
Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
2.Java中的可见性
对于可见性,Java提供了volatile关键字
来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见。
注意:
代码显示:
public class TestVolatile {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start();
while (true) {
if (td.isFlag()) {
System.out.println("------------------");
break;
}
}
}
}
class ThreadDemo implements Runnable {
volatile private boolean flag = false;
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
flag = true;
System.out.println("flag=" + isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
CAS算法:比较并交换,是一种硬件支持,比使用synchronized加锁的方式,更加的减少资源的使用.
下面的链接时关于CAS算法的详细的说明。
https://www.jianshu.com/p/21be831e851e
synchronized
悲观锁,存在互斥性
volatile
和CAS算法
都是乐观锁,不存在互斥性
[外链图片转存失败(img-Nq1FVaPH-1564485057675)(C:\Users\Administrator\Desktop\线程的运行状态图.jpg)]
public class Demo {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
System.out.println("方法1执行了");
}
}.start();
}
}
public class Demo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("方式2执行了");
}
}) {
}.start();
}
}
是一个容器,装有线程对象的容器
为什么要线程池?
虽然线程是个轻量级的东西, 但是对于互联网应用来说,如果每个用户的请求都创建一个线程,那会非常得多,服务器也是难于承受,再说了,众多的线程去竞争CPU,不断切换,也会让CPU调度不堪重负,很多线程将不得不等待。所以前辈们的思路就是(1)用少量的线程 (2) 让线程保持忙碌,就是说只创建一定数量的线程,让这些线程去处理所有的任务,任务执行完了以后,线程并不结束,而是回到线程池中去,等待接受下一个任务。
jdk5开始新新增了一个executors工厂类来产生线程池,有如下方法:
public static ExecutorService newCachedThreadPool():
根据任务的数量来创建线程对应的线程个数public static ExecutorService newFixedThreadPool(int nThreads):
固定初始化几个线程public static ExecutorService newSingleThreadExecutor():
初始化一个线程的线程池代码实现:
public class Myrunnable implements Runnable{
@Override
public void run() {
System.out.println("这个线程执行了");
}
}
public class Demo {
public static void main(String[] args) {
Myrunnable myrunnable = new Myrunnable();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(myrunnable);
}
}
使用callable的方法(可以获取返回值)
创建线程池对象
创建Callable实例
提交Callable实例 submit方法
关闭线程池
代码实现
public class Mycallable implements Callable {
String s="你好";
@Override
public Object call() throws Exception {
System.out.println("这是一个线程");
return s;
}
}
public class Demo {
public static void main(String[] args) {
Mycallable mycallable = new Mycallable();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(mycallable);
}
}
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。
public void schedule(TimerTask task, long delay)
延长多长时间后执行
public void schedule(TimerTask task,long delay,long period);
往后延长多长时间后间隔period时长重复执行
public void schedule(TimerTask task, Date time);
延长到日期后执行
public void schedule(TimerTask task, Date firstTime, long period)
:延长到日期后执行间隔period时长重复执行
public abstract void run()
public boolean cancel()
取消执行任务
案例演示:
public class Mytimertask1 extends TimerTask {
Timer timer;
public Mytimertask1(Timer timer) {
this.timer = timer;
}
@Override
public void run() {
System.out.println("这条语句执行了");
timer.cancel();
}
}
public class Test {
public static void main(String[] args) throws ParseException {
Timer timer = new Timer();
String str="2019-07-30 14:56:00";
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str);
Mytimertask1 mytimertask1 = new Mytimertask1(timer);
timer.schedule(mytimertask1,date);
}
}
public class Student {
//饿汉式
private static Student student=new Student();
//创建一个静态的对象,静态的对象只能调用一次
private Student(){};
//私有化无参构造
public static Student getStudent(){
//创建一个静态的方法
return student;
}
}
public class TEst {
public static void main(String[] args) {
Student student = Student.getStudent();
}
}
懒汉式
public class Teacher {
//懒汉式
private Teacher teacher=null;
private Teacher() {};
//私有化无参构造
public synchronized Teacher getTeacher(){
//乐观锁可以防止多线程产生的问题
if (teacher==null){
Teacher teacher = new Teacher();
}
return teacher;
}
}