(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)好好活就是做有意义的事情.
(8)亡羊补牢,为时未晚
(9)科技领域,没有捷径与投机取巧。
(10)有实力,一年365天都是应聘的旺季,没实力,天天都是应聘的淡季。
(11)基础不牢,地动天摇
(12)写博客初心:成长自己,辅助他人。当某一天离开人世,希望博客中的思想还能帮人指引方向.
(13)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~
线程基本使用入门
文章目录
1.多线程与Android线程性能优化2
1.1类锁对象锁显示锁
1.1.1什么是锁?
(1)锁,即大门,很多陌生人,就进不去,很安全。
(2)如果没有锁,无法锁这个大门,很多人,陌生人,都可以进入,就不安全。
(3)人进去后,再锁住(线程进去后再锁定),那么在外面的很多陌生人就进不去。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TEQpWFVx-1615884417568)(./1.png)]
生活当中上厕所
1.1.2synchronized为什么是隐式锁?
因为锁定与解锁,都看不见,也改不了,属于JDK内置锁。
1.1.3锁的分类
(1)内置锁
持有一把锁,(GpsEngine.class对象锁简称类锁)
(2)显示锁
1.1.3.1类锁
package lock;
public class GpsEngine {
private static GpsEngine gpsEngine;
public static GpsEngine getGpsEngine(){
if(null == gpsEngine){
//Thread-0 CPU执行器被操作系统调度给释放【暂停】【获得CPU执行权 恢复】 继续往下走
gpsEngine = new GpsEngine();
}
return gpsEngine;
}
//如果多个线程或者多个地方调用
//多线程执行的时候,需要非常注意,安全性问题
//synchronized:为什么很多开发者都说这是隐式锁?
// 因为锁定与解锁,都看不见,也改不了,属于JDK内置锁。
//持有的一把锁,GpsEngine.class对象锁 == 类锁
public static synchronized GpsEngine getGpsEngine2(){
if(null == gpsEngine){
//上锁之后 Thread-0 此时,Thread-1 Thread-2 Thread-3都进不来
gpsEngine = new GpsEngine();
}
return gpsEngine;
}//只有thread-0执行完毕 释放锁
//双重检测
public static GpsEngine getInstance(){
if(null == gpsEngine){
synchronized (GpsEngine.class){
if(null == gpsEngine){
gpsEngine = new GpsEngine();
}
}
}
return gpsEngine;
}
}
1.1.3.2对象锁
public class SynTest {
private long count = 0;
private static class Count extends Thread{
private SynTest simplOper;
public Count(SynTest simplOper){
this.simplOper = simplOper;
}
@Override
public void run() {
for (int i = 0 ; i < 10000; i++){
simplOper.incCount3();
}
}
}
//累加方法
//synchronized == 对象锁 持有一把锁,SynTest.this
private synchronized void incCount1() {
count ++;
}
private void incCount2() {
synchronized (SynTest.this){
count ++;
}
}
//随便持有一把锁 对象锁
private Object object = new Object();
private String str = new String();
private void incCount3(){
synchronized (str){
count ++;
}
}
public static void main(String[] args) throws InterruptedException {
SynTest simpleOper = new SynTest();
//启动两个线程累加
Count count1 = new Count(simpleOper);
Count count2 = new Count(simpleOper);
count1.start();//累加到10000
count2.start();//累加到20000
Thread.sleep(50);
System.out.println(simpleOper.count);
}
}
1.1.3.3显示锁
//Lock,显示锁,程序员可控制,锁定与解锁等逻辑
public class LockDemo {
private int count = 0;
//显示锁
private Lock lock = new ReentrantLock();
public void incr(){
try {
//1.开发者自己锁定
lock.lock();
//2.如果此句话发生异常,后面的解锁将无法被执行,就没法释放锁,导致其他线程无法进入执行
count ++;
}finally {
//3.开发者自己解锁,放在finally中,确保线程的锁一定得到释放
lock.unlock();
}
}
private static class Count extends Thread{
private LockDemo simplOper;
public Count(LockDemo simplOper){
this.simplOper = simplOper;
}
@Override
public void run() {
for (int i = 0 ; i < 10000; i++){
simplOper.incr();
}
}
}
public static void main(String[] args) throws InterruptedException {
LockDemo lockDemo = new LockDemo();
Count count1 = new Count(lockDemo);
Count count2 = new Count(lockDemo);
count1.start();
count2.start();
Thread.sleep(50);
System.out.println(lockDemo.count);
}
}
1.1.3.3.1可重入锁
(1)ReentrantLock:可重入锁
(2)synchronized:也是可重入锁
/**
* 可重入锁就是在递归的时候,可以反复拿到锁
* 如果synchronized默认就是可重入锁
* 如果synchronized不是可重入锁,会怎么样?递归将无法拿到锁,造成卡死状态。
*/
public synchronized void add(){
count ++;
add();
}
1.2多线程之生产者消费者案例一
//容器 资源 描述
public class Res {
//面包名称
private String name;
//编号
private int id;
//生产,存入
public void put(String name){
id +=1;
this.name = String.format("%s商品编号:%s",name,id);
System.out.println(Thread.currentThread().getName() + " ===生产者生产了: " + this.name);
}
//消费取出
public void out(){
id -= 1;
System.out.println(Thread.currentThread().getName() + " ===消费者消费了: " + this.name);
}
}
//生产任务
public class ProduceRunnable implements Runnable{
private Res res;
public ProduceRunnable(Res res){
this.res = res;
}
@Override
public void run() {
res.put("面包");
}
}
//消费者任务
public class ConsumeRunnable implements Runnable{
private Res res;
public ConsumeRunnable(Res res) {
this.res = res;
}
@Override
public void run() {
res.out();
}
}
/*
1.多线程中,如果没有加锁,基本上都会有安全性问题
*/
public class ThreadCommunicationDemo {
public static void main(String[] args) {
//1.创建资源对象
Res res = new Res();
//2.创建生产者任务
ProduceRunnable produceRunnable = new ProduceRunnable(res);
//3.创建消费者任务
ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);
//启动生产者
new Thread(produceRunnable).start();
//启动消费者
new Thread(consumeRunnable).start();
}
}
1.3多线程之生产者消费者案例二
(1)增加内置锁解决安全问题
//容器 资源 描述
public class Res {
//面包名称
private String name;
//编号
private int id;
//生产,存入
public synchronized void put(String name){
id +=1;
this.name = String.format("%s商品编号:%s",name,id);
System.out.println(Thread.currentThread().getName() + " ===生产者生产了: " + this.id);
}
//消费取出
public synchronized void out(){
System.out.println(Thread.currentThread().getName() + " ===消费者消费了: " + this.id);
id -= 1;
}
}
1.4等待唤醒机制原理
/**
* 描述资源
*/
class Res3 {
private int id;
private String name;
/**
* 定义标记目的是为了默认先运行生产者,先生产,后消费
*/
private boolean flag;
public synchronized void put(String name) throws InterruptedException {
//生产之前判断标记
if (!flag) {
//1.开始生产
id += 1; System.out.println(String.format("%s 生产者生产了 %d", Thread
.currentThread()
.getName(), this.id));
//2.生产完成
flag = true;
//3.唤醒 wait()线程,即唤醒消费线程消费,被冻结的线程,如果没有冻结任何线程,没有任何关系,Java默认不会报错
//3.1注意:wait或者notify必须要有锁包裹着。
notify();
//3.2当前自己线程会被冻结掉,释放CPU执行资格,释放CPU执行权,这个时候,CPU会去执行其他线程了。
wait();
}
}
public synchronized void out() throws InterruptedException {
if (flag) {
//1.开始消费
System.out.println(String.format("%s 消费者消费了 %d", Thread
.currentThread()
.getName(), this.id));
//2.消费完成
flag = false;
//3.唤醒wait()线程
notify();
//4.当前自己线程冻结
wait();
}
}
}
class ProduceRunnable implements Runnable {
private Res3 res;
public ProduceRunnable(Res3 res) {
this.res = res;
}
@Override
public void run() {
try {
for (int i = 0; i < 20; i++) {
res.put("面包");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ConsumeRunable implements Runnable {
private Res3 res;
public ConsumeRunable(Res3 res) {
this.res = res;
}
@Override
public void run() {
try {
for (int i = 0; i < 20; i++) {
res.out();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadCommunicationDemo {
public static void main(String[] args) {
Res3 res = new Res3();
ProduceRunnable p = new ProduceRunnable(res);
ConsumeRunable c = new ConsumeRunable(res);
new Thread(p).start();
new Thread(c).start();
}
}
等待区域:wait();
需要获取对象的锁
sync(持有的锁 对象的锁){
wait();
}
通知区域:notify()
获取对象的锁
sync(持有的锁 对象的锁){
notify()
}
注意:wait与notify持有的锁是同一把锁。
唤醒目标明确的时候就使用notify();不明确就使用notifiyAll();
1.5ThreadLocal的使用
线程的隔离Handler
(1)多个线程操作ThreadLocal对象,类似于只是对ThreadLocal中操作对象(Person)的复制本做操作,以此避免多个线程同时操作一共享对象时,引发的线程安全问题。
源码层的设计是每个线程去获取值,只是以当前线程作为key,去获取一份儿与当前线程对应的Value.对这个ThreadLocal对象做操作,只是对线程所对应对象做处理,不会影响到其他线程的修改。
(2)案例:
public class ThreadLocalTest2 {
class Person{
private String name;
private String sex;
private int age;
public Person() {
}
public Person(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + ", age='" + age + '\'' + '}';
}
}
ThreadLocal<Person> threadLocal = new ThreadLocal<Person>(){
@Override
protected Person initialValue() {
Person p = new Person("张三","男",20);
return p;
}
};
class StudentThread extends Thread{
@Override
public void run() {
String threadName = currentThread().getName();
System.out.println(String.format("首次获取threadName: %s get: %s",threadName,
threadLocal.get()));
Person p = new Person("王六","男",23);
threadLocal.set(p);
System.out.println(String.format("重设之后threadName: %s get: %s",threadName,
threadLocal.get()));
}
}
class TeacherThread extends Thread{
@Override
public void run() {
String threadName = currentThread().getName();
System.out.println(String.format("首次获取threadName: %s get: %s",threadName,
threadLocal.get()));
Person p = new Person("李雪","女",21);
threadLocal.set(p);
System.out.println(String.format("重设之后threadName: %s get: %s",threadName,
threadLocal.get()));
}
}
public static void main(String[] args) {
ThreadLocalTest2 outer = new ThreadLocalTest2();
outer.new StudentThread().start();
outer.new TeacherThread().start();
System.out.println(String.format("主线程threadName: %s get: %s",
Thread.currentThread().getName(),
outer.threadLocal.get()));
}
}
(3)执行结果
首次获取threadName: Thread-0 get: Person{name=‘张三’, sex=‘男’, age=‘20’}
首次获取threadName: Thread-1 get: Person{name=‘张三’, sex=‘男’, age=‘20’}
重设之后threadName: Thread-0 get: Person{name=‘王六’, sex=‘男’, age=‘23’}
重设之后threadName: Thread-1 get: Person{name=‘李雪’, sex=‘女’, age=‘21’}
2.打赏鼓励
感谢您的细心阅读,您的鼓励是我写作的不竭动力!!!