volatile关键字的作用是:使变量在多个线程间可见(可见性)
将变量的值维护在内存中,可以确保每个线程的操作访问到的值都是来自内存,可以保证多个线程读取到的是同一份数据
当两个线程读取一个共享变量是,他会默认加载到本线程的私有内存中,从私有内存中读取变量的值的时候!volatile关键字就强制当前线程更新该变量值为当前内存中维护的这个值!--这是我的理解如果错误请告知!!
解决线程安全问题,就是synchronize关键字,在多个线程对同一个变量进行访问时,做一下控制,只让一个线程来操作,这样就不会出现线程安全问题
所以说:volatile 可以保证可见性,但不能保证原子性(一个操作不可分割,比如事务的特性)。某种意义上是线程不安全的
线程的具体
定义子类继承Thread:重写run()方法;在main方法中创建定义子类,调用start()方法
Thread类是线程类继承他就会是一个线程对象;
run()相当于一个规定一个模板把代码放入方法内;
不能调用run()方法;调用run方法相当于是个单线程程序:
start方法用来开启线程,虚拟机会自动寻找run方法;
run()本身就是个普通方法等着虚拟机去执行他,调用run方法仅仅只是去执行他!
创建线程程序,就是让程序单独运行;开启一个新的栈内存空间去执行run()方法
代码如下:
/**
* 定义子类,继承Thread
* 重写run方法
*/
class SubThread extends Thread{
public void run() {
for (int i=0;i<700;i++){
System.out.println("run方法"+i);
}
}
}
/**
* 创建和启动一个线程
* 创建Thread子类对象
* 子类对象调用方法start()
*/
class ThreadDemo{
public static void main(String[] args) {
SubThread subThread = new SubThread();
subThread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main方法"+i);
}
}
}
获取线程名,设置县成名
/**
* 定义子类,继承Thread
* 重写run方法
* 获取线程名字,父类方法有个 getName()
*/
class SubThread extends Thread{
public void run() {
System.out.println(getName());
}
}
/**
* 创建和启动一个线程
* 创建Thread子类对象
* 子类对象调用方法start()
*/
class ThreadDemo{
public static void main(String[] args) {
SubThread subThread = new SubThread();
//用子类调用设置线程方法
// subThread.setName("大")
subThread.start();
}
}
运行结果:
Thread-0 设置之后的话是 大
Process finished with exit code 0
线程中的静态sleep(Long 毫秒值)方法,如果生命在run方法内只能try{}Cathy{}
class ThreadSleepDemo{
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
//需要声明异常
Thread.sleep(200);
System.out.println(i);
}
}
}
实现线程另一种方式:
接口的实现;实现接口Runnerable重写run()方法
分离了任务与线程对象,降低程序耦合性,实现了资源的共享
匿名内部类实现方式代码如下:
/**
* 实现接口方式的线程
* 匿名内部类的实现方法
*/
class SubRunnerAbleDemo{
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Thread---" + i);
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Runnable---" + i);
}
}
}).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
}
}
}
线程状态:
新建状态 new Thread() --> 运行状态 start()方法 --> run方法完成 死亡状态(stop()方法也可以使线程结束已经过时不推荐;可以设置一个boolean的变量来控制线程的结束)
受阻塞状态(cpu繁忙;同步锁;程序无法控制)具有CPU的执行资格等待CPU的资源;
等待休眠状态(运行到方法才会休眠sleep方法;有可能转到阻塞有可能到运行状态去)
无限等待状态(等待方法wait()方法在Object类中唤醒方法notify()方法)
线程池:
线程多的话创建线程销毁线程浪费资源,所以要创建一个线程池来存放线程减小资源开销!
程序一开始创建多个线程对象,当需要线程的时候就从线程中取出来;跑完线程之后回到容器中在放到容器中
jdk5内置线程池技术直接使用;
使用线程池的方式:通过线程池工厂生产线程对象
concurrent包中,有executors : 线程池创建工厂类
/**
* jdk1.5新特性,实现线程池程序
* 使用工厂类中的 Executors中的静态方法创建线程对象
* static ExecutorService newFixedThreadPoll
* 返回一个线程池对象ExecutorService接口的实现类
* 接口实现类的对象,调用submit(Runnerable r) 来提交线程任务
*
*/
class ThreadPool{
public static void main(String[] args) {
//调用工厂类静态方法,创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程提交任务");
System.out.println(Thread.currentThread().getName());
}
}));
}
}
Callable接口线程的第三种实现方式
/**
* 实现线程程序的第三种方式,实现Callable接口方式的静态方法
* 使用工厂类中的 Executors中的静态方法创建线程对象
* static ExecutorService newFixedThreadPoll
* 返回一个线程池对象ExecutorService接口的实现类
* 接口实现类的对象,调用submit(Callable c) 来提交线程任务
*
*/
class ThreadPoolDeme1{
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService ex = Executors.newFixedThreadPool(3);
Future<String> stringFuture = ex.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "call方法执行";
}
});
String s = stringFuture.get();
System.out.println(s);
}
}
控制带输出
"C:\Program Files\Java\jdk1.8.0_162\bin\java.exe" "-javaagent:C:\Program
call方法执行
案例:使用多线程技术,求和
两个线程,1个线程计算1+100之间的和;另一个线程实现1+200的和;多线程的异步计算
class ThreadCallabledemo implements Callable<Integer>{
private Integer a;
public ThreadCallabledemo( Integer a){
this.a=a;
}
@Override
public Integer call() throws Exception {
//定义求和变量;
int sum=0;
for (int i = 1; i <= a; i++) {
sum+=i;
}
return sum;
}
}
class TestCallable{
public static void main(String[] args) throws ExecutionException, InterruptedException {
//调用自定义求和方法
getSumBynum(100);
getSumBynum(200);
}
//求和方法
public static void getSumBynum(Integer a) throws ExecutionException, InterruptedException {
//创建线程池静态工厂
ExecutorService es = Executors.newFixedThreadPool(2);
Future<Integer> submit = es.submit(new ThreadCallabledemo(a));
System.out.println(submit.get());
}
}
线程安全问题:多个线程同时运性同一个共享数据
对线程同时并发访问同一个共享组员
使用同步技术解决线程安全问题!
1.同步代码块
公式:synchronize(任意对象){
线程共享数据
]}
在方法加上synchronize让这个方法变成同步方法;同步方法对象锁对应本类对象引用this
静态方法中同步有锁,是本类自己;
2.concurrent.locaks包中Lock接口
Lock接口可以替代synchronize
首先创建接口的实现类对象;
Lock lock = new ReentrantLock();
方法:lock() 获取锁 unLock() 释放锁
多线程死锁:多个同步锁嵌套,锁的顺序放的位置不正确;
由于两个线程间相互等待各自的锁,并且不释放,就会导致程序一直等待下去,发生死锁;
所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
在多线程编程中,为了避免发生这样的情况,可以有两种方法进行预防,这样可以避免死锁的发生;
- 加锁顺序一致 如果一个线程(比如线程3)需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。
- 避免锁未释放的场景 一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁
多线程下一边写入一边读取(等待与唤醒)
写操作先获得执行权,(添加一个变量 Boolean flag 如果为真说明赋值已经完成,为假获取值完成,所以需不需要赋值看标记)
标记具体: flag=true 说明写入赋值完成 flag=false 读取值完成
输入:需要赋值看标记标记为true赋值成功那么就等待 wait()方法,false 读取完成,不需要等待唤醒写入线程notify()让读取线程等待wait()方法
写入完成后必须等待,等到读出结束后才能下一次写入
写入完成赋值之后,执行wait()方法永远等待
读出操作:读出变量值后,必须等待,等待写入成功充重新变量中取出进行下一次读出
变量读出后在读出wait()方法等待之前唤醒写入的线程notify()方法
写入被唤醒后对变量重新赋值,赋值后先唤醒读出的线程,然后在使用wait()方法进入无限等待